@jskit-ai/agent-docs 0.1.293 → 0.1.294

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 (47) hide show
  1. package/guide/agent/app-extras/realtime.md +14 -4
  2. package/guide/agent/app-setup/multi-homing.md +12 -3
  3. package/guide/agent/framework/client-features.md +28 -0
  4. package/guide/agent/framework/crud-operations.md +7 -0
  5. package/guide/agent/framework/data-access.md +75 -9
  6. package/guide/agent/framework/sanity-checks.md +4 -4
  7. package/guide/agent/framework/typed-features.md +12 -3
  8. package/package.json +1 -1
  9. package/patterns/minimal-foundation/example/package.json +2 -2
  10. package/patterns/shell-foundation/example/package.json +3 -3
  11. package/reference/autogen/PATTERN_INDEX.md +35 -35
  12. package/reference/autogen/packages/agent.md +4 -2
  13. package/reference/autogen/packages/assistant-core.md +1 -1
  14. package/reference/autogen/packages/assistant-runtime.md +1 -0
  15. package/reference/autogen/packages/crud-core.md +2 -0
  16. package/reference/autogen/packages/http-web.md +9 -6
  17. package/reference/autogen/packages/realtime.md +1 -0
  18. package/reference/autogen/packages/resource-runtime.md +1 -0
  19. package/reference/autogen/packages/users-core.md +3 -0
  20. package/reference/autogen/packages/workspaces-core.md +11 -2
  21. package/reference/autogen/packages/workspaces-web.md +1 -1
  22. package/reference/autogen/tooling/config-eslint.md +26 -0
  23. package/reference/autogen/tooling/jskit-catalog.md +27 -13
  24. package/skills/jskit/references/client-features.md +28 -0
  25. package/skills/jskit/references/crud-operations.md +7 -0
  26. package/skills/jskit/references/data-access.md +75 -9
  27. package/skills/jskit/references/heuristics/INDEX.md +1 -1
  28. package/skills/jskit/references/heuristics/data-access.md +1 -0
  29. package/skills/jskit/references/heuristics/filters.md +14 -0
  30. package/skills/jskit/references/heuristics/server-search.md +2 -0
  31. package/skills/jskit/references/multi-homing.md +12 -3
  32. package/skills/jskit/references/pattern-index.md +35 -35
  33. package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +2 -2
  34. package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +3 -3
  35. package/skills/jskit/references/patterns/auth/accounts-hub/example/package.json +1 -1
  36. package/skills/jskit/references/patterns/auth/oidc-sign-in/example/package.json +2 -2
  37. package/skills/jskit/references/patterns/auth/supabase-auth/example/package.json +1 -1
  38. package/skills/jskit/references/patterns/connectors/calendar-cli/example/package.json +2 -2
  39. package/skills/jskit/references/patterns/crud/crud-feature-package/example/packages/books/package.json +2 -2
  40. package/skills/jskit/references/patterns/crud/crud-screen-set/PATTERN.md +9 -0
  41. package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +2 -2
  42. package/skills/jskit/references/patterns/users/user-administration-server/PATTERN.md +7 -3
  43. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +3 -3
  44. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +5 -5
  45. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/src/server/workspaceMembersOnly.js +69 -17
  46. package/skills/jskit/references/sanity-checks.md +4 -4
  47. package/skills/jskit/references/typed-features.md +12 -3
@@ -250,6 +250,13 @@ mapping in the resource; do not add page-local filtering or duplicate request
250
250
  code. The full Books source example maps `q` to title, author and notes; narrow
251
251
  it when adapting the example to fewer fields.
252
252
 
253
+ Every CRUD list also takes `id`, one record id or a list of them
254
+ (`filter[id]` on the JSON:API transport), and answers only those of its rows,
255
+ in the caller's scope. A picker whose selected values are not on the page it
256
+ loaded reads them this way, all in one request, instead of showing their raw
257
+ ids or reading pages until it finds them; `useCrudListFilterLookups()` does it
258
+ for a lookup filter's chips and options.
259
+
253
260
  The browser page's route query and internal CRUD query use plain `q`. The
254
261
  JSON:API HTTP transport uses `filter[q]`. Shared CRUD clients encode and decode
255
262
  this automatically. Raw HTTP/API tests must send the transport key themselves:
@@ -60,7 +60,9 @@ A grouped `aggregate` answers its groups from one statement, at most
60
60
  `pageLimits().request` of them (the resource's `queryMaxLimit`). When there
61
61
  are more it throws a 400 coded `RESOURCE_GROUP_LIMIT` instead of answering
62
62
  part of them. Narrow the filters, or read the groups in pages, `page: { size,
63
- number }` with `number` from 1, until a page answers fewer than `size` rows.
63
+ number }` with `number` from 1. A page answers as a `query` page does, `{ rows,
64
+ page: { size, number, hasMore, next } }`, never a bare array that could pass
65
+ for every group: read the next while `page.hasMore`.
64
66
  Page numbers are offsets, so read every page before work that changes what
65
67
  the filters match.
66
68
 
@@ -710,12 +712,12 @@ takes first.
710
712
  // good: the lock is the transaction's first read, so the bookings read after it are current
711
713
  const reserve = (seatId, options) => seats.withTransaction(options, async (txOptions) => {
712
714
  await seats.lock(seatId, { ...txOptions, fields: ["status"] });
713
- const taken = await bookings.queryAll({ filters: { seatIds: [seatId] } }, txOptions);
714
- return taken.length > 0 ? null : bookings.create({ seatId }, { ...txOptions, returning: "id" });
715
+ const [taken] = await bookings.queryAll({ filters: { seatIds: [seatId] }, take: 1 }, txOptions);
716
+ return taken ? null : bookings.create({ seatId }, { ...txOptions, returning: "id" });
715
717
  });
716
718
 
717
719
  // bad: the read before the lock fixed the snapshot, so a booking committed while the lock waited is missed
718
- const free = await bookings.queryAll({ filters: { seatIds: [seatId] } }, txOptions);
720
+ const [booked] = await bookings.queryAll({ filters: { seatIds: [seatId] }, take: 1 }, txOptions);
719
721
  await seats.lock(seatId, txOptions);
720
722
  ```
721
723
 
@@ -926,7 +928,11 @@ async function activeDays(memberIds, options) {
926
928
  `page.number` reads with OFFSET, which slows as the rows grow, so anything
927
929
  that grows pages with `page.after`. Each page is one statement, with no
928
930
  count: never walk pages to reach one.
929
- - A screen list never uses `queryAll`.
931
+ - A screen list never uses `queryAll`, and neither does a picker: it reads
932
+ one page searched on the server as the person types (`q`), narrowed in the
933
+ query by what it depends on (the parent record, `active`), and reads its
934
+ selected values past that page by id. Reading every row for a dropdown, or
935
+ narrowing a page in code, either grows with the table or misses rows.
930
936
  - Related rows come by id list (`filters: { id: ids }`, or an `in` filter on
931
937
  another field) or `include`, grouped in memory. Never a query per row, however
932
938
  the loop is spelled (`for`, `map` with `Promise.all`, a helper called per
@@ -982,6 +988,66 @@ const { records: firstPage } = await subscriptions.query({ filters: { newsletter
982
988
 
983
989
  Bad: a list read 100-row pages in a loop for each of 40 related sets.
984
990
 
991
+ ## Narrow in the query
992
+
993
+ A read asks the store for exactly the rows the code uses. The filter, sort,
994
+ limit, existence check and count go in the read, never in code over the rows
995
+ it answered: a read of every row then narrowed is slow, and a page or a `take`
996
+ then narrowed is also wrong, since the row wanted can sit past it.
997
+
998
+ - Filter with the resource's searchSchema: a field
999
+ (`filters: { status: "open" }`), an id list (`filters: { id: ids }`, or an
1000
+ `in` filter on another field), or `q` for typed text, whose `oneOf` names the
1001
+ fields it searches.
1002
+ - One row: a unique filter with `take: 1` or `page: { size: 1 }`, which also
1003
+ answers whether one exists. A count: `aggregate()`.
1004
+ - Never filter, find, slice or count the rows a read answered, nor skip some
1005
+ in a loop. Mapping them and grouping them by a key (`Map.groupBy`) narrow
1006
+ nothing, and neither does code that needs every row it read: rows handed on
1007
+ whole (returned, passed, stored, mapped or walked in full) with a subset
1008
+ derived beside them, and rows matched against other items in a loop or a
1009
+ callback (an in-memory join, one read instead of one per item).
1010
+ - A lookup field's picker and a list's Search box send what the person types
1011
+ as `q` to the server, which searches every row; `useList` does with
1012
+ `search: { enabled: true }`. The table resource defines `searchSchema.q`.
1013
+ Never search the page a list loaded in the browser: it misses every row past
1014
+ it.
1015
+ - A CRUD list refuses a query key it does not take with a 400
1016
+ (`crud.query.undeclaredKeys`). A list filter is declared in the feature's
1017
+ `listFilterQueryValidator` and the resource's searchSchema.
1018
+ - Prove a capped read with a test that seeds past the cap and makes the row
1019
+ the cap leaves out (the oldest, for a newest-first read) the one that
1020
+ matches: the read that asks for it finds it; a read narrowed in code does
1021
+ not.
1022
+
1023
+ ```js
1024
+ // good: the store answers one module's sections
1025
+ const { records } = await sections.query({ filters: { moduleId }, sort: ["-createdAt"], page: { size: 50 } }, options);
1026
+
1027
+ // bad: the newest 200 sections of every module, then one module's kept in code
1028
+ const { records: recent } = await sections.query({ sort: ["-createdAt"], page: { size: 200 } }, options);
1029
+ const moduleSections = recent.filter((section) => section.moduleId === moduleId);
1030
+
1031
+ // good: one row says who holds a unique username
1032
+ const [holder] = await profiles.queryAll({ filters: { usernames: [username] }, take: 1 }, options);
1033
+ ```
1034
+
1035
+ `jskit/narrow-in-query` reports `.filter()`, `.find()`, `.some()`,
1036
+ `.every()`, `.slice()`, a lone `.length` and a loop that skips rows with
1037
+ `continue` over the rows a read answered in the same function, unless the
1038
+ code also uses those rows whole, runs the narrowing once per item of
1039
+ something else, read at most one row, or slices a read that asked for one row
1040
+ more than it keeps (`limit(size + 1)`) to learn whether more follow.
1041
+ `jskit/search-on-server` reports a client search over a list's loaded items.
1042
+ `jskit check` fails a lookup field or a CRUD list screen whose app table
1043
+ resource has no `searchSchema.q`, on the resource or its owns entry
1044
+ (`data/search-q`). Other code that needs every row a read answers says why on
1045
+ the line above the read or the narrowing:
1046
+
1047
+ ```js
1048
+ // readable-check: allow narrow-in-query - every row: a conversation holds at most keep + 1 summaries, since each save deletes the older ones
1049
+ ```
1050
+
985
1051
  ## What changed since
986
1052
 
987
1053
  A page that catches up on a chat, a watch that acts on changes and a sync to
@@ -1077,10 +1143,10 @@ const SettlementFeature = defineFeature({
1077
1143
  async function settleDue(options) {
1078
1144
  const size = cohorts.pageLimits().request;
1079
1145
  const due = [];
1080
- for (let number = 1; ; number += 1) {
1081
- const page = await cohorts.aggregate({ measures: { cohorts: { count: "id" } }, groupBy: ["workspaceId"], filters: { status: "finished" }, page: { size, number } }, options);
1082
- due.push(...page);
1083
- if (page.length < size) break;
1146
+ for (let number = 1; number; ) {
1147
+ const { rows, page } = await cohorts.aggregate({ measures: { cohorts: { count: "id" } }, groupBy: ["workspaceId"], filters: { status: "finished" }, page: { size, number } }, options);
1148
+ due.push(...rows);
1149
+ number = page.next;
1084
1150
  }
1085
1151
  for (const { workspaceId } of due) {
1086
1152
  await settleWorkspace({ context: systemContext("settle finished weekly leagues for every user").forWorkspace(workspaceId) });
@@ -38,7 +38,7 @@ How to use it:
38
38
  - [ui-contract.md](./ui-contract.md)
39
39
  - filter, filters, search facets, chips, date range, enum filter, lookup filter, `useCrudListFilters`, `createCrudListFilters`
40
40
  - [filters.md](./filters.md)
41
- - `searchSchema`, `search: true`, `applyFilter`, server search, query validators, backend filters, internal JSON REST filters
41
+ - `searchSchema`, `search: true`, `applyFilter`, server search, query validators, backend filters, internal JSON REST filters, picker search, `q`, filtering rows in code, `jskit/narrow-in-query`, `jskit/search-on-server`, `data/search-q`, `crud.query.undeclaredKeys`
42
42
  - [server-search.md](./server-search.md)
43
43
  - `actualField`, `storage.column`, `storage.writeSerializer`, `storage.virtual`, `queryFields`, computed field, virtual field, projection, `defaultTo`, datetime write serialization, temporalPrecision, json-rest-schema dates
44
44
  - [table-resource-fields.md](./table-resource-fields.md)
@@ -13,6 +13,7 @@ Pick the layer ([Data access](../data-access.md)):
13
13
  - Two requests could change the same rows: `lockMany` inside `withTransaction`, rows in id order.
14
14
  - Work inside `withTransaction`: `txOptions` is only `{ context, trx }`, so a key the work needs, the app's own included, comes from the method's `options`, and a write on the method's own table gets `{ ...options, ...txOptions }` so the caller's `returning`, `missing` or `format` reaches it.
15
15
  - A change only while a row still holds a value (open to booked, once): `patch` with `onlyIf`, or `patchWhere` with that value in its filters (a compare-and-set on every database, so of two concurrent writers one wins); a filter on a related table is refused on MySQL and MariaDB (`selection_cross_table`), so filter on the row's own columns, after locking the related rows with `lockMany`, or use `claim`.
16
+ - Only some of the rows, one row, whether one exists: in the read (a searchSchema filter, `take: 1` or a page of size 1), never `.filter()`, `.find()`, `.some()`, `.slice()` or `.length` over the rows it answered ([Narrow in the query](../data-access.md#narrow-in-the-query)).
16
17
  - Counts, sums, buckets: `aggregate`; newest per group: a window include; anything else: one `scopedQuery()` method under `// Knex: <kind> - <reason>`.
17
18
  - Every workspace or user at once: a job with `systemScope`, finding its units with an `aggregate` grouped by the ownership field and looping over them, never rows.
18
19
  - A table another feature owns: its service or owner API, never its table. A report that joins it joins the `{ query, column }` its owner API answers from `scopedQuery({ alias })`, as a subquery: one statement, both scopes kept.
@@ -132,6 +132,20 @@ Use `useCrudListFilterLookups(...)` when:
132
132
  - the UI needs remote autocomplete search
133
133
  - chips should show readable labels instead of raw ids
134
134
 
135
+ A lookup filter's options are one server page, searched on the server as the
136
+ person types (`setSearch`, sent as `q`):
137
+ - the page keeps the server's order while more rows remain (`hasMore`), and is
138
+ sorted by label only once every row is loaded; `loadMore()` (or
139
+ `loadMoreLookup(key)`) reads the next page, so never present the first page
140
+ as the complete list
141
+ - a selected value past the loaded page (from the URL or a bookmark) is read by
142
+ id, every such value in one request, so its chip shows its label
143
+ - the options load when something shows them (the open filter sheet's
144
+ control, or a search), not with every list page
145
+ - a lookup narrowed by another value (only active suppliers, only one
146
+ region's) passes it in `requestQueryParams[filterKey]`, which the lookup's
147
+ list must accept as a filter
148
+
135
149
  Use built-in `presence` when:
136
150
  - a filter means null vs not-null, such as assigned vs unassigned storage
137
151
  - the UI wants custom labels like "Assigned" / "Unassigned" but the transport contract can stay `present` / `missing`
@@ -243,6 +243,8 @@ owns: [{ resource: contactResource, rowPolicy: ownContactsOrShared }]
243
243
  - Put visibility in the owner's row policy, never in a search key or route query validation.
244
244
  - Do not expose a query key publicly just because the service maps to it internally.
245
245
  - Do not rely on route validators alone; a public key still needs a matching backend search definition.
246
+ - A CRUD list refuses a query key its route does not declare with a 400 (`crud.query.undeclaredKeys`), so an undeclared filter fails loudly instead of answering every row.
247
+ - Never narrow the rows a read answered in code, and never search a list's loaded page in the browser: the filter goes in the read, and a picker or Search box sends `q` ([Narrow in the query](../data-access.md#narrow-in-the-query); `jskit/narrow-in-query`, `jskit/search-on-server`, and `jskit check`'s `data/search-q` for a lookup or list screen whose resource has no `searchSchema.q`).
246
248
 
247
249
  ## Quick Rule Of Thumb
248
250
 
@@ -196,12 +196,21 @@ method takes the call's `options` last and joins the transaction of the
196
196
  | Capability | Methods |
197
197
  | --- | --- |
198
198
  | `workspaces` | `findById(id, options)`, `findBySlug(slug, options)` (a deleted workspace is not found, though its slug stays taken), `listSlugsIn`, `findPersonalByOwnerUserId`, `listForUserId(userId, options)`, `insert`, `updateById`, `lock(workspaceId, options)` |
199
- | `workspaces.memberships` | `findByWorkspaceIdAndUserId`, `ensureOwnerMembership`, `upsertMembership(workspaceId, userId, patch, options)`, `addMember({ workspaceId, userId, roleSid }, options)`, `activeMemberIds(workspaceId, options)`, `listActiveByWorkspaceId`, `findActiveByWorkspaceIdAndUserId`, `listActiveWorkspaceIdsByUserId`, `listActiveWorkspaceIdsByUserIds` |
199
+ | `workspaces.memberships` | `findByWorkspaceIdAndUserId`, `ensureOwnerMembership`, `upsertMembership(workspaceId, userId, patch, options)`, `addMember({ workspaceId, userId, roleSid }, options)`, `activeMemberIds(workspaceId, options)`, `listActiveByWorkspaceId`, `findActiveByWorkspaceIdAndUserId`, `listActiveWorkspaceIdsByUserId`, `listActiveWorkspaceIdsByUserIds`, `listActiveWorkspaceIdsByUserIdsAmong(userIds, workspaceIds, options)`, `joinSource({ alias, filters }, options)` |
200
200
 
201
201
  `addMember` makes the user an active member with the role (`member` when none
202
202
  is given), adding the membership or reactivating it. `activeMemberIds` answers
203
- the active members' user ids at the same cost however many members there
204
- are, without including their profiles. `lock` takes
203
+ every active member's user id, without their profiles: it reads all of the
204
+ workspace's active memberships, so its cost grows with the workspace, and so
205
+ do `listActiveByWorkspaceId`'s and
206
+ `workspaces.services.members.listMembers(workspace, options)`'s. Use them where
207
+ the work needs every member, such as a job, a delivery's recipients or a
208
+ realtime audience. A screen or list reads a page instead:
209
+ `workspaces.services.members.listMembersPage(workspace, { cursor, limit }, options)`
210
+ answers one page of active members by display name (`limit`, 20 when none)
211
+ with the `nextCursor` that asks for the next, null on the last, and an app's
212
+ own list of members pages them in SQL with one report over the memberships'
213
+ `joinSource`. `lock` takes
205
214
  the workspace's row lock until the caller's transaction ends, so work that must
206
215
  not interleave for one workspace takes turns; it runs only inside a
207
216
  transaction, given its `txOptions`, and answers `{ id }`, or `null` when there
@@ -15,7 +15,7 @@ A concrete Fastify, Vue, and JSKIT application foundation for products that do n
15
15
 
16
16
  - Id: `app/minimal-foundation`
17
17
  - Keywords: `app`, `fastify`, `foundation`, `minimal`, `server`, `vite`, `vue`
18
- - Owner: `@jskit-ai/agent-docs@0.1.293`
18
+ - Owner: `@jskit-ai/agent-docs@0.1.294`
19
19
  - Read: [PATTERN.md](patterns/app/minimal-foundation/PATTERN.md)
20
20
  - Examples: [example/](patterns/app/minimal-foundation/example/)
21
21
  - Requires: `@jskit-ai/http-runtime`, `@jskit-ai/kernel`
@@ -26,7 +26,7 @@ A concrete JSKIT web application foundation with responsive shell navigation, se
26
26
 
27
27
  - Id: `app/shell-foundation`
28
28
  - Keywords: `app`, `foundation`, `material`, `navigation`, `placements`, `shell`, `vite`, `vue`
29
- - Owner: `@jskit-ai/agent-docs@0.1.293`
29
+ - Owner: `@jskit-ai/agent-docs@0.1.294`
30
30
  - Read: [PATTERN.md](patterns/app/shell-foundation/PATTERN.md)
31
31
  - Examples: [example/](patterns/app/shell-foundation/example/)
32
32
  - Requires: `@jskit-ai/http-runtime`, `@jskit-ai/kernel`, `@jskit-ai/shell-web`
@@ -37,7 +37,7 @@ Configure an assistant runtime for one application surface and expose its chat a
37
37
 
38
38
  - Id: `assistant/assistant-surface`
39
39
  - Keywords: `ai`, `assistant`, `chat`, `config`, `environment`, `page`, `placement`, `settings`, `surface`
40
- - Owner: `@jskit-ai/assistant-runtime@0.2.39`
40
+ - Owner: `@jskit-ai/assistant-runtime@0.2.40`
41
41
  - Read: [PATTERN.md](patterns/assistant/assistant-surface/PATTERN.md)
42
42
  - Examples: [example/](patterns/assistant/assistant-surface/example/)
43
43
  - Requires: `@jskit-ai/assistant-runtime`, `@jskit-ai/connectors-catalog`, `@jskit-ai/connectors-core`, `@jskit-ai/shell-web`
@@ -48,7 +48,7 @@ Run one OpenID Connect sign-in for all of an owner's apps as its own JSKIT proje
48
48
 
49
49
  - Id: `auth/accounts-hub`
50
50
  - Keywords: `accounts hub`, `auth`, `authentication`, `issuer`, `master login`, `oidc`, `openid connect`, `single sign-on`, `sso`
51
- - Owner: `@jskit-ai/oidc-issuer-core@0.1.26`
51
+ - Owner: `@jskit-ai/oidc-issuer-core@0.1.27`
52
52
  - Read: [PATTERN.md](patterns/auth/accounts-hub/PATTERN.md)
53
53
  - Examples: [example/](patterns/auth/accounts-hub/example/)
54
54
  - Requires: `@jskit-ai/oidc-issuer-core`
@@ -59,7 +59,7 @@ Compose JSKIT authentication routes, views, profile controls, and public surface
59
59
 
60
60
  - Id: `auth/auth-surface`
61
61
  - Keywords: `account`, `auth`, `login`, `logout`, `password`, `placement`, `profile`, `reset`, `surface`
62
- - Owner: `@jskit-ai/auth-web@0.2.24`
62
+ - Owner: `@jskit-ai/auth-web@0.2.25`
63
63
  - Read: [PATTERN.md](patterns/auth/auth-surface/PATTERN.md)
64
64
  - Examples: [example/](patterns/auth/auth-surface/example/)
65
65
  - Requires: `@jskit-ai/auth-core`, `@jskit-ai/auth-web`, `@jskit-ai/shell-web`
@@ -70,7 +70,7 @@ Let people sign in with the accounts they already have in an accounts hub or ano
70
70
 
71
71
  - Id: `auth/oidc-sign-in`
72
72
  - Keywords: `accounts hub`, `auth`, `authentication`, `back-channel logout`, `dynamic client registration`, `issuer`, `master login`, `oidc`, `openid connect`, `single sign-on`, `sso`
73
- - Owner: `@jskit-ai/auth-provider-oidc-core@0.2.26`
73
+ - Owner: `@jskit-ai/auth-provider-oidc-core@0.2.27`
74
74
  - Read: [PATTERN.md](patterns/auth/oidc-sign-in/PATTERN.md)
75
75
  - Examples: [example/](patterns/auth/oidc-sign-in/example/)
76
76
  - Requires: `@jskit-ai/auth-provider-oidc-core`, `@jskit-ai/auth-web`
@@ -81,7 +81,7 @@ Add Supabase authentication through normal npm composition, explicit environment
81
81
 
82
82
  - Id: `auth/supabase-auth`
83
83
  - Keywords: `auth`, `authentication`, `oauth`, `sessions`, `supabase`
84
- - Owner: `@jskit-ai/auth-provider-supabase-core@0.2.20`
84
+ - Owner: `@jskit-ai/auth-provider-supabase-core@0.2.21`
85
85
  - Read: [PATTERN.md](patterns/auth/supabase-auth/PATTERN.md)
86
86
  - Examples: [example/](patterns/auth/supabase-auth/example/)
87
87
  - Requires: `@jskit-ai/auth-provider-supabase-core`
@@ -92,7 +92,7 @@ Resolve an administrator environment key or an individual user's grant, then use
92
92
 
93
93
  - Id: `connectors/ai-connections`
94
94
  - Keywords: `ai`, `api-key`, `cli`, `environment`, `free`, `models`, `ownership`, `zai`, `zen`
95
- - Owner: `@jskit-ai/connectors-catalog@0.2.23`
95
+ - Owner: `@jskit-ai/connectors-catalog@0.2.24`
96
96
  - Read: [PATTERN.md](patterns/connectors/ai-connections/PATTERN.md)
97
97
  - Examples: [example/](patterns/connectors/ai-connections/example/)
98
98
  - Requires: `@jskit-ai/connectors-catalog`, `@jskit-ai/connectors-core`
@@ -103,7 +103,7 @@ Compose provider operations, authorization and encrypted file storage from a CLI
103
103
 
104
104
  - Id: `connectors/api-key-connection`
105
105
  - Keywords: `api-key`, `clay`, `cli`, `clickhouse`, `connectors`, `dbt`, `files`, `firecrawl`, `granola`, `integrations`, `metrics`, `prestashop`, `resend`, `semantic-layer`, `semrush`, `x-twitter`
106
- - Owner: `@jskit-ai/connectors-catalog@0.2.23`
106
+ - Owner: `@jskit-ai/connectors-catalog@0.2.24`
107
107
  - Read: [PATTERN.md](patterns/connectors/api-key-connection/PATTERN.md)
108
108
  - Examples: [example/](patterns/connectors/api-key-connection/example/)
109
109
  - Requires: `@jskit-ai/connectors-catalog`, `@jskit-ai/connectors-core`
@@ -114,7 +114,7 @@ Compose token-based MCP discovery and explicitly authorized tool calls using the
114
114
 
115
115
  - Id: `connectors/assistant-mcp`
116
116
  - Keywords: `assistant`, `cli`, `connectors`, `files`, `integrations`, `mcp`, `n8n`, `sanity`
117
- - Owner: `@jskit-ai/connectors-catalog@0.2.23`
117
+ - Owner: `@jskit-ai/connectors-catalog@0.2.24`
118
118
  - Read: [PATTERN.md](patterns/connectors/assistant-mcp/PATTERN.md)
119
119
  - Examples: [example/](patterns/connectors/assistant-mcp/example/)
120
120
  - Requires: `@jskit-ai/connectors-catalog`, `@jskit-ai/connectors-core`
@@ -125,7 +125,7 @@ Configure assistant OAuth clients, then compose existing state, encrypted file g
125
125
 
126
126
  - Id: `connectors/assistant-mcp-oauth`
127
127
  - Keywords: `amplitude`, `assistant`, `atlassian`, `canva`, `cli`, `client metadata`, `confidence`, `connectors`, `figma`, `files`, `granola`, `hex`, `mcp`, `miro`, `oauth`
128
- - Owner: `@jskit-ai/connectors-catalog@0.2.23`
128
+ - Owner: `@jskit-ai/connectors-catalog@0.2.24`
129
129
  - Read: [PATTERN.md](patterns/connectors/assistant-mcp-oauth/PATTERN.md)
130
130
  - Examples: [example/](patterns/connectors/assistant-mcp-oauth/example/)
131
131
  - Requires: `@jskit-ai/connectors-catalog`, `@jskit-ai/connectors-core`
@@ -136,7 +136,7 @@ Compose signed AWS operations, file configuration and application authorization
136
136
 
137
137
  - Id: `connectors/aws-storage-queries`
138
138
  - Keywords: `athena`, `aws`, `cli`, `connectors`, `files`, `iam`, `queries`, `s3`, `storage`, `sts`
139
- - Owner: `@jskit-ai/connectors-catalog@0.2.23`
139
+ - Owner: `@jskit-ai/connectors-catalog@0.2.24`
140
140
  - Read: [PATTERN.md](patterns/connectors/aws-storage-queries/PATTERN.md)
141
141
  - Examples: [example/](patterns/connectors/aws-storage-queries/example/)
142
142
  - Requires: `@jskit-ai/connectors-catalog`, `@jskit-ai/connectors-core`
@@ -147,7 +147,7 @@ Compose reusable connection libraries with portable configuration, application o
147
147
 
148
148
  - Id: `connectors/calendar-cli`
149
149
  - Keywords: `calendar`, `cli`, `connectors`, `google`, `integrations`, `oauth`, `permissions`
150
- - Owner: `@jskit-ai/connector-google-calendar@0.2.23`
150
+ - Owner: `@jskit-ai/connector-google-calendar@0.2.24`
151
151
  - Read: [PATTERN.md](patterns/connectors/calendar-cli/PATTERN.md)
152
152
  - Examples: [example/](patterns/connectors/calendar-cli/example/)
153
153
  - Requires: `@jskit-ai/connector-google-calendar`, `@jskit-ai/connectors-core`, `@jskit-ai/database-runtime-mysql`
@@ -158,7 +158,7 @@ Compose the shared connector runtime for event delivery while leaving workflow c
158
158
 
159
159
  - Id: `connectors/event-delivery`
160
160
  - Keywords: `cli`, `connectors`, `events`, `files`, `inngest`, `integrations`, `workflows`
161
- - Owner: `@jskit-ai/connectors-catalog@0.2.23`
161
+ - Owner: `@jskit-ai/connectors-catalog@0.2.24`
162
162
  - Read: [PATTERN.md](patterns/connectors/event-delivery/PATTERN.md)
163
163
  - Examples: [example/](patterns/connectors/event-delivery/example/)
164
164
  - Requires: `@jskit-ai/connectors-catalog`, `@jskit-ai/connectors-core`
@@ -169,7 +169,7 @@ Wire service-account grants, file storage, explicit message actions and public b
169
169
 
170
170
  - Id: `connectors/firebase-messaging`
171
171
  - Keywords: `cli`, `connectors`, `fcm`, `files`, `firebase`, `integrations`, `jwt`, `notifications`, `push`, `service-account`, `vapid`
172
- - Owner: `@jskit-ai/connectors-catalog@0.2.23`
172
+ - Owner: `@jskit-ai/connectors-catalog@0.2.24`
173
173
  - Read: [PATTERN.md](patterns/connectors/firebase-messaging/PATTERN.md)
174
174
  - Examples: [example/](patterns/connectors/firebase-messaging/example/)
175
175
  - Requires: `@jskit-ai/connectors-catalog`, `@jskit-ai/connectors-core`
@@ -180,7 +180,7 @@ Compose reviewed Search campaigns with project-owned Google OAuth and portable s
180
180
 
181
181
  - Id: `connectors/google-ads-search`
182
182
  - Keywords: `ads`, `campaigns`, `cli`, `connectors`, `conversion`, `google`, `search`
183
- - Owner: `@jskit-ai/connectors-catalog@0.2.23`
183
+ - Owner: `@jskit-ai/connectors-catalog@0.2.24`
184
184
  - Read: [PATTERN.md](patterns/connectors/google-ads-search/PATTERN.md)
185
185
  - Examples: [example/](patterns/connectors/google-ads-search/example/)
186
186
  - Requires: `@jskit-ai/connectors-catalog`, `@jskit-ai/connectors-core`
@@ -191,7 +191,7 @@ Compose provider consent, verification and encrypted file persistence from an ap
191
191
 
192
192
  - Id: `connectors/oauth-connection`
193
193
  - Keywords: `accounting`, `assistant permissions`, `books`, `cli`, `client credentials`, `connectors`, `consent`, `crm`, `databricks`, `developer token`, `fabric`, `files`, `gaql`, `google ads`, `graphql`, `integrations`, `linkedin`, `microsoft`, `oauth`, `powerpoint`, `salesforce`, `service principal`, `shopify`, `soql`, `staffing`, `tiktok`, `twitch`, `wave`, `word`, `wordpress`, `workday`, `xero`, `zoho`
194
- - Owner: `@jskit-ai/connectors-catalog@0.2.23`
194
+ - Owner: `@jskit-ai/connectors-catalog@0.2.24`
195
195
  - Read: [PATTERN.md](patterns/connectors/oauth-connection/PATTERN.md)
196
196
  - Examples: [example/](patterns/connectors/oauth-connection/example/)
197
197
  - Requires: `@jskit-ai/connectors-catalog`, `@jskit-ai/connectors-core`
@@ -202,7 +202,7 @@ Compose a sequential catalogue batch and preserve partial success for applicatio
202
202
 
203
203
  - Id: `connectors/paddle-catalogue`
204
204
  - Keywords: `batch`, `paddle`, `payments`, `prices`, `products`
205
- - Owner: `@jskit-ai/connectors-catalog@0.2.23`
205
+ - Owner: `@jskit-ai/connectors-catalog@0.2.24`
206
206
  - Read: [PATTERN.md](patterns/connectors/paddle-catalogue/PATTERN.md)
207
207
  - Examples: [example/](patterns/connectors/paddle-catalogue/example/)
208
208
  - Requires: `@jskit-ai/connectors-catalog`, `@jskit-ai/connectors-core`
@@ -213,7 +213,7 @@ Resolve a publishable image key deliberately and use the shared URL library in a
213
213
 
214
214
  - Id: `connectors/public-image`
215
215
  - Keywords: `cli`, `connectors`, `files`, `images`, `integrations`, `logo-dev`, `public-key`
216
- - Owner: `@jskit-ai/connectors-catalog@0.2.23`
216
+ - Owner: `@jskit-ai/connectors-catalog@0.2.24`
217
217
  - Read: [PATTERN.md](patterns/connectors/public-image/PATTERN.md)
218
218
  - Examples: [example/](patterns/connectors/public-image/example/)
219
219
  - Requires: `@jskit-ai/connectors-catalog`, `@jskit-ai/connectors-core`
@@ -224,7 +224,7 @@ Compose file configuration, Redshift Data API operations and application query o
224
224
 
225
225
  - Id: `connectors/redshift-queries`
226
226
  - Keywords: `aws`, `cli`, `connectors`, `files`, `iam`, `provisioned`, `redshift`, `serverless`, `sql`, `warehouse`
227
- - Owner: `@jskit-ai/connectors-catalog@0.2.23`
227
+ - Owner: `@jskit-ai/connectors-catalog@0.2.24`
228
228
  - Read: [PATTERN.md](patterns/connectors/redshift-queries/PATTERN.md)
229
229
  - Examples: [example/](patterns/connectors/redshift-queries/example/)
230
230
  - Requires: `@jskit-ai/connectors-catalog`, `@jskit-ai/connectors-core`
@@ -235,7 +235,7 @@ Use the shared scanner runtime from a CLI or a host worker with portable configu
235
235
 
236
236
  - Id: `connectors/source-scanning`
237
237
  - Keywords: `cancellation`, `cli`, `connectors`, `integrations`, `scanning`, `security`, `source`, `wiz`, `worker`
238
- - Owner: `@jskit-ai/connectors-catalog@0.2.23`
238
+ - Owner: `@jskit-ai/connectors-catalog@0.2.24`
239
239
  - Read: [PATTERN.md](patterns/connectors/source-scanning/PATTERN.md)
240
240
  - Examples: [example/](patterns/connectors/source-scanning/example/)
241
241
  - Requires: `@jskit-ai/connectors-catalog`, `@jskit-ai/connectors-core`
@@ -246,7 +246,7 @@ Add a protected administration surface, settings shell, and semantic navigation
246
246
 
247
247
  - Id: `console/console-surface`
248
248
  - Keywords: `admin`, `console`, `navigation`, `owner`, `placement`, `settings`, `surface`
249
- - Owner: `@jskit-ai/console-web@0.1.250`
249
+ - Owner: `@jskit-ai/console-web@0.1.251`
250
250
  - Read: [PATTERN.md](patterns/console/console-surface/PATTERN.md)
251
251
  - Examples: [example/](patterns/console/console-surface/example/)
252
252
  - Requires: `@jskit-ai/console-core`, `@jskit-ai/console-web`, `@jskit-ai/shell-web`
@@ -257,7 +257,7 @@ Define an application table resource whose records belong to the current user, a
257
257
 
258
258
  - Id: `crud/crud-contract`
259
259
  - Keywords: `authenticated`, `crud`, `database`, `owner-scoped`, `resource`, `table`, `user`
260
- - Owner: `@jskit-ai/resource-crud-core@0.1.209`
260
+ - Owner: `@jskit-ai/resource-crud-core@0.1.210`
261
261
  - Read: [PATTERN.md](patterns/crud/crud-contract/PATTERN.md)
262
262
  - Examples: [example/](patterns/crud/crud-contract/example/)
263
263
  - Requires: `@jskit-ai/resource-core`, `@jskit-ai/resource-crud-core`
@@ -268,7 +268,7 @@ Build a complete application-owned CRUD server package from a migration, a table
268
268
 
269
269
  - Id: `crud/crud-feature-package`
270
270
  - Keywords: `actions`, `crud`, `database`, `defineCrudFeature`, `json-api`, `migration`, `owns`, `permissions`, `repository`, `routes`, `service`, `table resource`
271
- - Owner: `@jskit-ai/crud-core@0.2.23`
271
+ - Owner: `@jskit-ai/crud-core@0.2.24`
272
272
  - Read: [PATTERN.md](patterns/crud/crud-feature-package/PATTERN.md)
273
273
  - Examples: [example/](patterns/crud/crud-feature-package/example/)
274
274
  - Requires: `@jskit-ai/crud-core`, `@jskit-ai/resource-core`, `@jskit-ai/resource-crud-core`
@@ -279,7 +279,7 @@ Build a routed CRUD user interface as thin application pages over JSKIT's shared
279
279
 
280
280
  - Id: `crud/crud-screen-set`
281
281
  - Keywords: `actions`, `add`, `crud`, `delete`, `edit`, `filters`, `list`, `material`, `routes`, `view`, `vue`
282
- - Owner: `@jskit-ai/http-web@0.2.27`
282
+ - Owner: `@jskit-ai/http-web@0.2.28`
283
283
  - Read: [PATTERN.md](patterns/crud/crud-screen-set/PATTERN.md)
284
284
  - Examples: [example/](patterns/crud/crud-screen-set/example/)
285
285
  - Requires: `@jskit-ai/http-web`, `@jskit-ai/resource-core`, `@jskit-ai/resource-crud-core`
@@ -323,7 +323,7 @@ Wrap a JSKIT web application in a Capacitor Android shell using native Capacitor
323
323
 
324
324
  - Id: `mobile/android-application`
325
325
  - Keywords: `android`, `capacitor`, `device`, `mobile`, `native`, `shell`, `webview`
326
- - Owner: `@jskit-ai/mobile-capacitor@0.2.22`
326
+ - Owner: `@jskit-ai/mobile-capacitor@0.2.23`
327
327
  - Read: [PATTERN.md](patterns/mobile/android-application/PATTERN.md)
328
328
  - Examples: [example/](patterns/mobile/android-application/example/)
329
329
  - Requires: `@capacitor/android`, `@capacitor/app`, `@capacitor/cli`, `@jskit-ai/mobile-capacitor`
@@ -334,7 +334,7 @@ Add JSKIT realtime events with an optional Redis backplane and an explicit shell
334
334
 
335
335
  - Id: `realtime/realtime-application`
336
336
  - Keywords: `realtime`, `redis`, `socket.io`, `sockets`, `status`, `websocket`
337
- - Owner: `@jskit-ai/realtime@0.2.25`
337
+ - Owner: `@jskit-ai/realtime@0.2.26`
338
338
  - Read: [PATTERN.md](patterns/realtime/realtime-application/PATTERN.md)
339
339
  - Examples: [example/](patterns/realtime/realtime-application/example/)
340
340
  - Requires: `@jskit-ai/realtime`, `@jskit-ai/shell-web`
@@ -345,7 +345,7 @@ Compose application-owned Google Publisher Tag delivery with the generic rewarde
345
345
 
346
346
  - Id: `rewards/google-rewarded`
347
347
  - Keywords: `ads`, `google`, `publisher tag`, `rewarded`, `unlock`
348
- - Owner: `@jskit-ai/rewarded-web@0.2.28`
348
+ - Owner: `@jskit-ai/rewarded-web@0.2.29`
349
349
  - Read: [PATTERN.md](patterns/rewards/google-rewarded/PATTERN.md)
350
350
  - Examples: [example/](patterns/rewards/google-rewarded/example/)
351
351
  - Requires: `@jskit-ai/rewarded-web`
@@ -356,7 +356,7 @@ Define a server feature through explicit capabilities and first-class actions, a
356
356
 
357
357
  - Id: `server/feature-package`
358
358
  - Keywords: `actions`, `aggregate`, `feature`, `handle`, `orchestration`, `owns`, `package`, `provider`, `repository`, `routes`, `server`, `table resource`
359
- - Owner: `@jskit-ai/agent-docs@0.1.293`
359
+ - Owner: `@jskit-ai/agent-docs@0.1.294`
360
360
  - Read: [PATTERN.md](patterns/server/feature-package/PATTERN.md)
361
361
  - Examples: [example/](patterns/server/feature-package/example/)
362
362
  - Requires: `@jskit-ai/kernel`
@@ -367,7 +367,7 @@ Compose the JSKIT responsive shell, semantic placements, settings navigation, an
367
367
 
368
368
  - Id: `shell/application-shell`
369
369
  - Keywords: `adaptive`, `app`, `layout`, `navigation`, `placement`, `responsive`, `settings`, `shell`
370
- - Owner: `@jskit-ai/shell-web@0.2.22`
370
+ - Owner: `@jskit-ai/shell-web@0.2.23`
371
371
  - Read: [PATTERN.md](patterns/shell/application-shell/PATTERN.md)
372
372
  - Examples: [example/](patterns/shell/application-shell/example/)
373
373
  - Requires: `@jskit-ai/kernel`, `@jskit-ai/shell-web`
@@ -378,7 +378,7 @@ Add product routes and shell extensions through file routing, semantic placement
378
378
 
379
379
  - Id: `ui/page-and-placement`
380
380
  - Keywords: `component`, `navigation`, `outlet`, `page`, `placement`, `routes`, `section`, `shell`, `subpages`, `vue`
381
- - Owner: `@jskit-ai/shell-web@0.2.22`
381
+ - Owner: `@jskit-ai/shell-web@0.2.23`
382
382
  - Read: [PATTERN.md](patterns/ui/page-and-placement/PATTERN.md)
383
383
  - Examples: [example/](patterns/ui/page-and-placement/example/)
384
384
  - Requires: `@jskit-ai/kernel`, `@jskit-ai/shell-web`
@@ -389,7 +389,7 @@ Compose an account settings route and profile, preference, notification, and sig
389
389
 
390
390
  - Id: `users/account-settings`
391
391
  - Keywords: `account`, `link provider`, `notifications`, `oauth`, `preferences`, `profile`, `settings`, `sign-in methods`, `user`, `vue`
392
- - Owner: `@jskit-ai/users-web@0.2.33`
392
+ - Owner: `@jskit-ai/users-web@0.2.34`
393
393
  - Read: [PATTERN.md](patterns/users/account-settings/PATTERN.md)
394
394
  - Examples: [example/](patterns/users/account-settings/example/)
395
395
  - Requires: `@jskit-ai/shell-web`, `@jskit-ai/users-core`, `@jskit-ai/users-web`
@@ -400,7 +400,7 @@ Expose user administration and workspace-scoped member operations through app-ow
400
400
 
401
401
  - Id: `users/user-administration-server`
402
402
  - Keywords: `account`, `admin`, `member`, `repository`, `resource`, `routes`, `service`, `user`, `workspace`
403
- - Owner: `@jskit-ai/users-core@0.2.24`
403
+ - Owner: `@jskit-ai/users-core@0.2.25`
404
404
  - Read: [PATTERN.md](patterns/users/user-administration-server/PATTERN.md)
405
405
  - Examples: [example/](patterns/users/user-administration-server/example/)
406
406
  - Requires: `@jskit-ai/crud-core`, `@jskit-ai/users-core`
@@ -411,7 +411,7 @@ Configure roles, workspace access policy, invitations, and app-owned invitation
411
411
 
412
412
  - Id: `workspaces/workspace-server`
413
413
  - Keywords: `access`, `invite`, `membership`, `multitenancy`, `policy`, `role`, `tenancy`, `workspace`
414
- - Owner: `@jskit-ai/workspaces-core@0.2.24`
414
+ - Owner: `@jskit-ai/workspaces-core@0.2.25`
415
415
  - Read: [PATTERN.md](patterns/workspaces/workspace-server/PATTERN.md)
416
416
  - Examples: [example/](patterns/workspaces/workspace-server/example/)
417
417
  - Requires: `@jskit-ai/users-core`, `@jskit-ai/workspaces-core`
@@ -422,7 +422,7 @@ Compose workspace selection, invitation, member administration, settings, and re
422
422
 
423
423
  - Id: `workspaces/workspace-surfaces`
424
424
  - Keywords: `admin`, `invite`, `member`, `navigation`, `settings`, `surface`, `switcher`, `workspace`
425
- - Owner: `@jskit-ai/workspaces-web@0.2.33`
425
+ - Owner: `@jskit-ai/workspaces-web@0.2.34`
426
426
  - Read: [PATTERN.md](patterns/workspaces/workspace-surfaces/PATTERN.md)
427
427
  - Examples: [example/](patterns/workspaces/workspace-surfaces/example/)
428
428
  - Requires: `@jskit-ai/shell-web`, `@jskit-ai/workspaces-core`, `@jskit-ai/workspaces-web`
@@ -49,8 +49,8 @@
49
49
  "@vue/devtools-api": "^8.2.1"
50
50
  },
51
51
  "devDependencies": {
52
- "@jskit-ai/config-eslint": "0.1.249",
53
- "@jskit-ai/jskit-catalog": "0.2.45",
52
+ "@jskit-ai/config-eslint": "0.1.250",
53
+ "@jskit-ai/jskit-catalog": "0.2.46",
54
54
  "@playwright/test": "1.61.1",
55
55
  "@vitejs/plugin-vue": "^6.0.7",
56
56
  "eslint": "^10.8.0",
@@ -45,13 +45,13 @@
45
45
  "vue-router": "^5.3.1",
46
46
  "vuetify": "^4.3.0",
47
47
  "@jskit-ai/http-runtime": "0.2.18",
48
- "@jskit-ai/shell-web": "0.2.22",
48
+ "@jskit-ai/shell-web": "0.2.23",
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.249",
54
- "@jskit-ai/jskit-catalog": "0.2.45",
53
+ "@jskit-ai/config-eslint": "0.1.250",
54
+ "@jskit-ai/jskit-catalog": "0.2.46",
55
55
  "@playwright/test": "1.61.1",
56
56
  "@vitejs/plugin-vue": "^6.0.7",
57
57
  "eslint": "^10.8.0",
@@ -4,6 +4,6 @@
4
4
  "dependencies": {
5
5
  "@jskit-ai/database-runtime": "0.1.267",
6
6
  "@jskit-ai/database-runtime-mysql": "0.1.265",
7
- "@jskit-ai/oidc-issuer-core": "0.1.26"
7
+ "@jskit-ai/oidc-issuer-core": "0.1.27"
8
8
  }
9
9
  }
@@ -2,7 +2,7 @@
2
2
  "private": true,
3
3
  "type": "module",
4
4
  "dependencies": {
5
- "@jskit-ai/auth-provider-oidc-core": "0.2.26",
6
- "@jskit-ai/auth-web": "0.2.24"
5
+ "@jskit-ai/auth-provider-oidc-core": "0.2.27",
6
+ "@jskit-ai/auth-web": "0.2.25"
7
7
  }
8
8
  }
@@ -2,6 +2,6 @@
2
2
  "private": true,
3
3
  "type": "module",
4
4
  "dependencies": {
5
- "@jskit-ai/auth-provider-supabase-core": "0.2.20"
5
+ "@jskit-ai/auth-provider-supabase-core": "0.2.21"
6
6
  }
7
7
  }
@@ -7,8 +7,8 @@
7
7
  "db:migrate:status": "knex --knexfile ./knexfile.js migrate:list"
8
8
  },
9
9
  "dependencies": {
10
- "@jskit-ai/connectors-core": "0.2.23",
11
- "@jskit-ai/connector-google-calendar": "0.2.23",
10
+ "@jskit-ai/connectors-core": "0.2.24",
11
+ "@jskit-ai/connector-google-calendar": "0.2.24",
12
12
  "@jskit-ai/database-runtime": "0.1.267",
13
13
  "@jskit-ai/database-runtime-mysql": "0.1.265",
14
14
  "knex": "^3.1.0"
@@ -9,9 +9,9 @@
9
9
  "./shared": "./src/shared/index.js"
10
10
  },
11
11
  "dependencies": {
12
- "@jskit-ai/crud-core": "0.2.23",
12
+ "@jskit-ai/crud-core": "0.2.24",
13
13
  "@jskit-ai/resource-core": "0.1.209",
14
- "@jskit-ai/resource-crud-core": "0.1.209"
14
+ "@jskit-ai/resource-crud-core": "0.1.210"
15
15
  },
16
16
  "jskit": {
17
17
  "kind": "runtime",
@@ -134,6 +134,15 @@ A form field whose `relation` is a `lookup` takes its options from
134
134
  lookup label for a selected value missing from the current options, that option
135
135
  is listed first; a selected value without a label is not listed.
136
136
 
137
+ A lookup whose options depend on another field of the form (the rooms of the
138
+ chosen building, only bookable ones) narrows them on the server: pass
139
+ `requestQueryParams` keyed by the field, `createCrudLookupFieldRuntime({
140
+ formFields, requestQueryParams: { roomId: () => ({ buildingId:
141
+ formState.buildingId, bookable: true }) } })`. The function runs again when what
142
+ it reads changes, so the options follow the building. Each value is a filter the lookup's list accepts
143
+ (its `searchSchema` and list filter validator), never a page of every row
144
+ narrowed in the browser.
145
+
137
146
  Render selection fields whose options load from the server, or whose value is a
138
147
  record id, with `SelectField` from
139
148
  `@jskit-ai/http-web/client/components/SelectField`, not a raw `v-select`,