@jskit-ai/agent-docs 0.1.105 → 0.1.107

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.
@@ -1336,8 +1336,8 @@ If the filter contract should be shared with server code, promote it into a CRUD
1336
1336
 
1337
1337
  - create `packages/contacts/src/shared/contactListFilters.js`
1338
1338
  - create `packages/contacts/src/server/contactListFilterContract.js` with `createCrudListFilterContract(...)`
1339
- - update `packages/contacts/src/server/registerRoutes.js` so the list route query validator includes `contactListFilterContract.queryValidator`
1340
- - update `packages/contacts/src/server/actions.js` so the list action input validator includes the same `queryValidator`
1339
+ - update `packages/contacts/src/server/registerRoutes.js` so `createCrudJsonApiRouteContracts(...)` receives `listFilterQueryValidator: contactListFilterContract.queryValidator`
1340
+ - update `packages/contacts/src/server/actions.js` so `createStandardCrudListQueryValidators(...)` receives the same `listFilterQueryValidator`
1341
1341
  - update the provider's `createJsonRestResourceScopeOptions(...)` call so it merges `searchSchema: contactListFilterContract.jsonRestSearchSchema`
1342
1342
  - update `packages/contacts/src/server/repository.js` so the list query path passes `contactListFilterContract.toJsonRestQuery(query)` into `buildJsonRestQueryParams(...)`
1343
1343
 
@@ -1466,21 +1466,49 @@ That one contract gives the server:
1466
1466
  - `toJsonRestQuery(query)` for repository query normalization
1467
1467
  - `applyQuery(...)` when a non-JSON REST repository still needs direct Knex filtering
1468
1468
 
1469
- Wire the contract into route and action validators:
1469
+ Wire the contract independently into the standard route and action query
1470
+ groups:
1470
1471
 
1471
1472
  ```js
1472
- const listRouteQueryValidator = composeSchemaDefinitions([
1473
- listCursorPaginationQueryValidator,
1474
- listSearchQueryValidator,
1475
- listParentFilterQueryValidator,
1476
- contactListFilterContract.queryValidator,
1477
- lookupIncludeQueryValidator
1478
- ], {
1479
- mode: "patch"
1473
+ const {
1474
+ listRouteContract
1475
+ } = createCrudJsonApiRouteContracts({
1476
+ resource,
1477
+ listFilterQueryValidator: contactListFilterContract.queryValidator
1480
1478
  });
1481
1479
  ```
1482
1480
 
1483
- Use the same `contactListFilterContract.queryValidator` anywhere else the list query is validated, such as the composed list action input validator if your CRUD package validates query shape at both the route and action boundaries.
1481
+ ```js
1482
+ input: composeSchemaDefinitions([
1483
+ workspaceSlugParamsValidator,
1484
+ ...createStandardCrudListQueryValidators({
1485
+ resource,
1486
+ listFilterQueryValidator: contactListFilterContract.queryValidator
1487
+ })
1488
+ ])
1489
+ ```
1490
+
1491
+ This does not couple route and action layers together. Each layer still owns
1492
+ its validator; both consume the same explicit standard group so pagination,
1493
+ search, parent filters, includes, typed sparse fieldsets, and structured
1494
+ filters cannot drift.
1495
+
1496
+ If `resource.contract.listFilters.queryValidator` already owns the filter
1497
+ validator, call `createStandardCrudListQueryValidators({ resource })` instead.
1498
+ Append a validator after the standard group only for genuinely additional,
1499
+ non-filter query input. Never provide the same filter validator through both
1500
+ paths.
1501
+
1502
+ Do not manually reconstruct the standard list group from its individual
1503
+ validators. Standard view actions likewise compose:
1504
+
1505
+ ```js
1506
+ input: composeSchemaDefinitions([
1507
+ workspaceSlugParamsValidator,
1508
+ recordIdParamsValidator,
1509
+ ...createStandardCrudViewQueryValidators()
1510
+ ])
1511
+ ```
1484
1512
 
1485
1513
  Merge the JSON REST search schema when the provider registers the resource:
1486
1514
 
@@ -1532,6 +1560,13 @@ Choose the invalid-value contract deliberately:
1532
1560
  - Keep client-only filters in the generated page-local `listFilters.js`. Move definitions into a CRUD package only when server code or another page needs to share them.
1533
1561
  - Keep the filter keys identical all the way through: definition key, query param key, and repository meaning.
1534
1562
  - Prefer `createCrudListFilterContract(...)` for server-backed structured filters so route/action validators, JSON REST search schema, and repository query normalization stay derived from one shared definition.
1563
+ - Compose standard list and view query validators with
1564
+ `createStandardCrudListQueryValidators(...)` and
1565
+ `createStandardCrudViewQueryValidators()` rather than repeating the
1566
+ individual standard validators.
1567
+ - Pass a server-backed structured-filter validator through
1568
+ `listFilterQueryValidator`; append validators separately only for
1569
+ additional non-filter query input.
1535
1570
  - Use `type: "presence"` for null/not-null filters such as assigned vs unassigned storage. Do not model those as custom enums plus `applyQuery(...)` overrides unless the SQL semantics are genuinely different from `whereNotNull(...)` / `whereNull(...)`.
1536
1571
  - Use `createCrudListFilters(...)` directly only for non-JSON REST repository code that needs direct Knex filtering without JSON REST registration.
1537
1572
  - Use `q` for free-text and explicit query params for structured filters.
@@ -424,6 +424,22 @@ The same rule applies after later server scaffolds such as `addresses` and `comm
424
424
 
425
425
  For standard CRUDs, that file is intentionally compact. It uses `defineCrudResource(...)` from `@jskit-ai/resource-crud-core`, authors the canonical `schema` / `searchSchema` / `defaultSort` / `autofilter` shape once, and lets JSKIT derive the standard CRUD operation contracts from it.
426
426
 
427
+ The generated server action validators are compact for the same reason.
428
+ Standard list actions compose
429
+ `createStandardCrudListQueryValidators({ resource })`; standard view actions
430
+ compose `createStandardCrudViewQueryValidators()`. Do not expand those groups
431
+ back into individual pagination, search, parent-filter, include, or
432
+ sparse-field validators.
433
+
434
+ When a CRUD adds a server-backed structured-filter contract, pass its
435
+ `queryValidator` through the list group's dedicated
436
+ `listFilterQueryValidator` option. If
437
+ `resource.contract.listFilters.queryValidator` already owns it, `{ resource }`
438
+ is sufficient. A validator belongs after the standard group only when it is
439
+ genuinely additional, non-filter query input. This keeps route and action
440
+ validation independent while preventing the two boundaries from drifting as
441
+ the standard query contract evolves.
442
+
427
443
  ### Step 3: scaffold the UI
428
444
 
429
445
  Once the resource file exists, generate the UI route tree:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/agent-docs",
3
- "version": "0.1.105",
3
+ "version": "0.1.107",
4
4
  "description": "Distributed JSKIT agent references, prompts, guides, and generated reference maps.",
5
5
  "type": "module",
6
6
  "files": [
@@ -60,19 +60,58 @@ Server-side pattern, only when implementing real backend filter semantics:
60
60
  1. Put reusable filter definitions in the CRUD package if server code or multiple pages need the same contract.
61
61
  Example path: `packages/<crud>/src/shared/<crud>ListFilters.js`
62
62
  2. Build a server contract from that module with `createCrudListFilterContract(...)`.
63
- 3. Use the contract's `queryValidator` in route/action input composition.
63
+ 3. Pass the contract's `queryValidator` through the dedicated
64
+ `listFilterQueryValidator` option of the standard CRUD list validator group
65
+ at both the route and action boundaries.
64
66
  4. Pass the contract's `jsonRestSearchSchema` into `createJsonRestResourceScopeOptions(..., { searchSchema })`.
65
67
  5. Call `contract.toJsonRestQuery(query)` before `buildJsonRestQueryParams(...)` in the JSON REST repository path.
66
68
 
67
69
  Exact file checklist:
68
70
  - create `packages/<crud>/src/shared/<crud>ListFilters.js`
69
71
  - create `packages/<crud>/src/server/<crud>ListFilterContract.js` with `createCrudListFilterContract(...)`
70
- - update `packages/<crud>/src/server/registerRoutes.js` and `packages/<crud>/src/server/actions.js` so the list query validator includes `listFilterContract.queryValidator`
72
+ - update `packages/<crud>/src/server/registerRoutes.js` so
73
+ `createCrudJsonApiRouteContracts(...)` receives
74
+ `listFilterQueryValidator: listFilterContract.queryValidator`
75
+ - update `packages/<crud>/src/server/actions.js` so
76
+ `createStandardCrudListQueryValidators(...)` receives the same
77
+ `listFilterQueryValidator`
71
78
  - update the provider's `createJsonRestResourceScopeOptions(...)` call so `searchSchema: listFilterContract.jsonRestSearchSchema` is merged into the internal JSON REST resource
72
79
  - update `packages/<crud>/src/server/repository.js` so list queries pass `listFilterContract.toJsonRestQuery(query)` into `buildJsonRestQueryParams(...)`
73
80
  - update the generated page-local `listFilters.js` first; only edit `index.vue` if a specialist lookup label/runtime integration is needed
74
81
  - for lookup-backed filters, wire `useCrudListFilterLookups(...)` beside the existing generated filter runtime instead of replacing `CrudListFilterSurface`
75
82
 
83
+ Standard route and action query composition:
84
+
85
+ ```js
86
+ const {
87
+ listRouteContract
88
+ } = createCrudJsonApiRouteContracts({
89
+ resource,
90
+ listFilterQueryValidator: recordsListFilterContract.queryValidator
91
+ });
92
+ ```
93
+
94
+ ```js
95
+ input: composeSchemaDefinitions([
96
+ workspaceSlugParamsValidator,
97
+ ...createStandardCrudListQueryValidators({
98
+ resource,
99
+ listFilterQueryValidator: recordsListFilterContract.queryValidator
100
+ })
101
+ ])
102
+ ```
103
+
104
+ If `resource.contract.listFilters.queryValidator` already owns the filter
105
+ validator, pass only `{ resource }`. Append a validator after the standard
106
+ group only for genuinely additional, non-filter query input. Never supply the
107
+ same filter validator through both paths.
108
+
109
+ Do not reconstruct the standard list group from individual pagination,
110
+ search, parent-filter, include, or sparse-field validators. The group keeps
111
+ the independently validated route and action layers aligned as standard query
112
+ features evolve. Standard view actions use
113
+ `createStandardCrudViewQueryValidators()` for the same reason.
114
+
76
115
  Validation mode is part of the contract:
77
116
  - `createCrudListFilterContract(...)` defaults to `invalidValues: "reject"` for a strict server boundary
78
117
  - set `invalidValues` explicitly when a package is choosing a non-default validation posture
@@ -111,6 +150,8 @@ Avoid:
111
150
  - local filter composables that duplicate the same keys the server already knows about
112
151
  - a custom validator shape that does not match the page state
113
152
  - hand-rolled route/action validators or repository filters that duplicate `createCrudListFilterContract(...)`
153
+ - manually rebuilding the standard CRUD list/view validator groups from individual validators
154
+ - appending a list-filter validator after `createStandardCrudListQueryValidators(...)` when it belongs in the dedicated `listFilterQueryValidator` option
114
155
  - hand-rolled preset apply/reset/active-state helpers when `useCrudListFilters(..., { presets })`, `applyPreset(...)`, and `matchesPreset(...)` fit
115
156
  - per-screen `useList()` wrappers for lookup-backed filters when `useCrudListFilterLookups(...)` fits
116
157
  - editing generated `.vue` files just to add basic filter controls; use the page-local `listFilters.js` seam first
@@ -139,6 +180,10 @@ Preset contract notes:
139
180
  Review checks:
140
181
  - one filter definition source of truth: generated page-local `listFilters.js` for client-only filters, or a shared CRUD-package module when server code imports the same definitions
141
182
  - server validator, JSON REST search schema, and repository query projection derived from that source through `createCrudListFilterContract(...)`
183
+ - route and action boundaries independently compose
184
+ `createStandardCrudListQueryValidators(...)`, using the dedicated
185
+ `listFilterQueryValidator` option when the resource does not already own it
186
+ - standard view actions compose `createStandardCrudViewQueryValidators()`
142
187
  - client query params/chips/reset logic derived from that source
143
188
  - initial route/default resolution completes before the first list request
144
189
  - lookup-backed filters use the shared lookup helper, not a page-local mini-framework
@@ -128,26 +128,40 @@ Keep `applyFilter` focused on query semantics:
128
128
  - do not put permission checks there
129
129
  - do not turn it into a second service layer
130
130
 
131
- Add route validators when:
131
+ Add public query validation when:
132
132
  - the filter key is public and may arrive from HTTP query params
133
133
  - the page/UI needs a stable, validated query contract
134
134
  - malformed values should either be rejected or deliberately discarded
135
135
 
136
- Example route layer:
136
+ For standard CRUD routes and actions, pass the structured-filter validator
137
+ through the standard group's dedicated option:
137
138
 
138
139
  ```js
139
140
  input: composeSchemaDefinitions([
140
141
  workspaceSlugParamsValidator,
141
- listCursorPaginationQueryValidator,
142
- listSearchQueryValidator,
143
- vetsListFiltersQueryValidator,
142
+ ...createStandardCrudListQueryValidators({
143
+ resource,
144
+ listFilterQueryValidator: vetsListFilterContract.queryValidator
145
+ })
144
146
  ])
145
147
  ```
146
148
 
147
149
  That means:
148
- - `q` and the structured filter keys are accepted publicly
150
+ - pagination, `q`, parent filters, includes, sparse fieldsets, and the
151
+ structured filter keys remain one standard group
149
152
  - the repository may still add internal-only keys later
150
153
 
154
+ Use the same `listFilterQueryValidator` option with
155
+ `createCrudJsonApiRouteContracts(...)` at the route boundary. The layers
156
+ remain independently validated; they share a small standard validator group
157
+ rather than duplicating its current members.
158
+
159
+ If `resource.contract.listFilters.queryValidator` already owns the filter
160
+ validator, call `createStandardCrudListQueryValidators({ resource })`.
161
+ Append validators separately only for genuinely additional, non-filter query
162
+ input. Standard view actions use
163
+ `createStandardCrudViewQueryValidators()`.
164
+
151
165
  For structured list filters, prefer the generated contract shape:
152
166
 
153
167
  ```js
@@ -229,5 +243,5 @@ function buildScopedQuery(query = {}, context = null) {
229
243
  - Same key, same field, simple behavior: `search: true`
230
244
  - Public alias or multi-field search: `searchSchema`
231
245
  - Complex SQL semantics: `searchSchema` + `applyFilter`
232
- - Public HTTP query key: add a route validator
246
+ - Public HTTP query key: add it through the standard route/action query group
233
247
  - Internal-only repository key: inject it in `repository.js`, skip the public validator