@jskit-ai/agent-docs 0.1.102 → 0.1.103

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.
@@ -444,6 +444,31 @@ That creates the baseline CRUD route tree:
444
444
  - `w/[workspaceSlug]/admin/contacts/[contactId]/edit.vue`
445
445
  - shared `_components` files under the same route root
446
446
 
447
+ Generated list, view, and lookup reads use the resource contract as their
448
+ response authority. They return every field declared for output by default,
449
+ including the target resource's declared output when a lookup relation is
450
+ hydrated; generated pages do not repeat those fields in a second page-owned
451
+ allowlist. Adding an output field or lookup to the resource therefore does not
452
+ require regenerating endpoint projections.
453
+
454
+ Put exceptional large fields in the resource-owned default response blacklist,
455
+ `resource.contract.response.defaultExclude`:
456
+
457
+ ```js
458
+ contract: {
459
+ response: {
460
+ defaultExclude: ["rawPayload", "largeAuditJson"]
461
+ }
462
+ }
463
+ ```
464
+
465
+ Each included resource applies its own blacklist. A default-excluded field is
466
+ still part of the public output contract and can be requested through an
467
+ explicit typed JSON:API `fields[type]` fieldset. Use that sparse-field override
468
+ only for a specialised caller; do not add one to ordinary generated pages or
469
+ build a page-local query-string adapter. Fields that must never be exposed do
470
+ not belong in the output schema at all.
471
+
447
472
  This is the most important mental model in the whole chapter:
448
473
 
449
474
  - `crud-server-generator` creates the reusable CRUD contract and server package
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/agent-docs",
3
- "version": "0.1.102",
3
+ "version": "0.1.103",
4
4
  "description": "Distributed JSKIT agent references, prompts, guides, and generated reference maps.",
5
5
  "type": "module",
6
6
  "files": [
@@ -49,6 +49,7 @@ Generated screen wrapper extension rules:
49
49
  - Use `useCrudListScreen({ readEnabled })` for permission-gated generated list reads instead of splitting the page or replacing the shared list screen.
50
50
  - Use `useCrudListScreen({ requestQueryParams })` for list includes or other endpoint query params instead of putting query strings in `apiSuffix`.
51
51
  - Use `useCrudViewScreen({ requestQueryParams })` for detail includes instead of putting query strings in `apiUrlTemplate`.
52
+ - Use `requestFieldsets` only when a specialised caller intentionally needs a typed JSON:API sparse fieldset. Ordinary generated reads use the complete resource output contract.
52
53
  - Use `readEnabled` and `queryKeyFactory` on `useCrudViewScreen()` when the detail read needs the same gating or cache identity control as `useCrudView()`.
53
54
  - Use `CrudViewScreen` slots (`before-fields`, `fields`, `after-fields`, `supporting-content`) for page-specific domain sections while keeping shared load/error/retry chrome.
54
55
  - Use `listRowActions` with `defineCrudListRowActions(...)` for row-level commands in `CrudListScreen`.
@@ -72,6 +73,11 @@ Why this is the standard JSKIT shape:
72
73
  - `usersWebHttpClient` already handles credentials and CSRF behavior.
73
74
  - `useEndpointResource()` is the shared endpoint primitive for loading, saving, and standard load/save error handling. Higher-level runtimes add UI feedback and field-error handling on top.
74
75
  - Use `requestQueryParams` for endpoint query strings on list, view, and add/edit runtimes.
76
+ - Generated CRUD and lookup reads use all resource-defined output fields by default. Hydrated relationships use the target resource's output contract. Generated pages and lookup controls do not repeat those definitions as request fieldsets.
77
+ - Put exceptional large fields in `resource.contract.response.defaultExclude`. The target resource owns that default even when it is included by another resource.
78
+ - `requestFieldsets` remains an explicit specialised override and accepts the canonical typed shape, for example `{ jobs: ["id", "status"], contacts: ["id", "displayName"] }`. It participates in both the request and the query cache key.
79
+ - Sparse fieldsets are a serialization boundary, not an authorization mechanism. Server resources reject unknown fields, never serialize hidden fields, and preserve the fields needed internally for relationship linkage.
80
+ - Fields that must never be exposed do not belong in the resource output schema.
75
81
  - Keep `apiUrlTemplate` path-only. Do not put `?include=...` or other query strings in URL templates.
76
82
  - If an app needs route-aware API URL rewriting, configure the users-web client once with `configureUsersWebHttpClient({ resolveRequestUrl })` before mounting the app. Do not replace `fetchImpl` just to rewrite paths.
77
83
  - `resolveRequestUrl` belongs at the HTTP client boundary. It runs after JSKIT encodes query params and before browser `fetch`, so reads, commands, request recovery metadata, JSON:API transport, credentials, and CSRF behavior stay on the standard path.
@@ -96,6 +102,8 @@ Avoid:
96
102
  - manually concatenating scoped route params into API URLs
97
103
  - using a lower-level seam when a higher-level routed CRUD or command runtime already fits
98
104
  - smuggling query params into `apiUrlTemplate`
105
+ - hand-building `fields[type]` parameters or page-local sparse-response adapters
106
+ - repeating resource output fields as page-owned request allowlists
99
107
  - replacing `CrudListScreen` or `CrudViewScreen` only to add read gating, row actions, synthetic display rows, includes, or domain detail sections
100
108
  - reporting routine resource load errors through hand-written global snackbar/banner calls
101
109
  - calling `useShellRequestRecoveryRuntime().report(...)` from normal panels just to recover an HTTP read
@@ -13,6 +13,7 @@ Ask first:
13
13
  - which fields are filterable
14
14
  - whether each filter is a flag, enum, multi-enum, date/date-range, number-range, record id, or lookup-backed record id
15
15
  - whether filters must sync to the route query
16
+ - whether a filter needs a default before the first request
16
17
  - whether the screen needs chips and clear/reset behavior
17
18
  - whether lookup filters need remote autocomplete search
18
19
  - whether there are presets such as "Today", "Last 7 Days", or "Only Archived"
@@ -25,6 +26,27 @@ Default JSKIT client pattern:
25
26
  5. The AI/app author is responsible for ensuring the server accepts and applies the query params declared in `listFilters.js`.
26
27
  6. For lookup-backed filters, use `useCrudListFilterLookups(...)` when the page needs remote options or readable chip labels.
27
28
 
29
+ Initial-value contract:
30
+ - declare a pre-query default with `defaultValue` in the filter definition
31
+ - a valid route-query value wins over `defaultValue`
32
+ - when the initial route has no value, `defaultValue` is applied before list reading is enabled
33
+ - an invalid initial route value falls back to `defaultValue`
34
+ - clearing a filter after initialization produces the empty value; it does not reapply the default
35
+ - route back/forward navigation rehydrates the same filter state without an early unfiltered request
36
+
37
+ ```js
38
+ const listFilters = defineCrudListFilters({
39
+ currentness: {
40
+ type: "enum",
41
+ defaultValue: "active",
42
+ options: [
43
+ { value: "active", label: "Active" },
44
+ { value: "archived", label: "Archived" }
45
+ ]
46
+ }
47
+ });
48
+ ```
49
+
28
50
  Generated client shape:
29
51
  - `src/pages/<surface>/<resource>/listFilters.js`
30
52
  - `const listFilters = defineCrudListFilters({ ... })`
@@ -94,6 +116,7 @@ Avoid:
94
116
  - editing generated `.vue` files just to add basic filter controls; use the page-local `listFilters.js` seam first
95
117
  - overloading `q` with structured filter meaning
96
118
  - inline filter-definition objects passed into `useCrudListFilters(...)`, `createCrudListFilters(...)`, or `createCrudListFilterContract(...)`; keep definitions in a named module
119
+ - assigning a default to `filterRuntime.values` after `useCrudListScreen(...)` has started; that changes the query after construction and can issue a second initial request
97
120
 
98
121
  Good shape:
99
122
  - `src/pages/home/customers/listFilters.js`
@@ -117,5 +140,6 @@ Review checks:
117
140
  - 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
118
141
  - server validator, JSON REST search schema, and repository query projection derived from that source through `createCrudListFilterContract(...)`
119
142
  - client query params/chips/reset logic derived from that source
143
+ - initial route/default resolution completes before the first list request
120
144
  - lookup-backed filters use the shared lookup helper, not a page-local mini-framework
121
145
  - the two-phase server exception is intentional and documented, not accidental drift
@@ -64,6 +64,7 @@ Exports
64
64
  - `parseCrudListRangeQueryExpression(value = null)`
65
65
  - `formatCrudListRangeQueryExpression(startValue = "", endValue = "", { collapseExact = false } = {})`
66
66
  - `defineCrudListFilters(definitions = {})`
67
+ - `createCrudListFilterEmptyValue(filter = {})`
67
68
  - `createCrudListFilterInitialValue(filter = {})`
68
69
  - `isCrudListFilterMultiValue(filter = {})`
69
70
  - `isCrudListFilterStructuredValue(filter = {})`
@@ -171,6 +172,12 @@ Exports
171
172
  - `shouldRetryTransientQueryFailure`
172
173
  - `transientQueryRetryDelay`
173
174
 
175
+ ### `support/jsonApiFieldsets.js`
176
+ Exports
177
+ - `normalizeJsonApiFieldList(value = [])`
178
+ - `normalizeJsonApiFieldsets(value = {}, { primaryType = "" } = {})`
179
+ - `buildJsonApiFieldsetsToken(value = {})`
180
+
174
181
  ### `support/linkPath.js`
175
182
  Exports
176
183
  - `isExternalLinkTarget(target = "")`
@@ -165,6 +165,7 @@ Exports
165
165
  - `createCrudCursorPaginationQueryValidator(list = {})`
166
166
  - `listSearchQueryValidator`
167
167
  - `lookupIncludeQueryValidator`
168
+ - `jsonApiFieldsetsQueryValidator`
168
169
  - `resolveCrudParentFilterKeys(resource = {})`
169
170
  - `createCrudParentFilterQueryValidator(resource = {})`
170
171
  Local functions
@@ -247,6 +247,9 @@ Local functions
247
247
  - `isRecord(value)`
248
248
  - `normalizeQueryKey(key = "")`
249
249
  - `buildFieldsTransportKey(responseType = "")`
250
+ - `parseFieldsTransportType(key = "")`
251
+ - `addJsonApiFieldset(fieldsets, type = "", value = [])`
252
+ - `createJsonApiFieldsTransportValueSchema()`
250
253
  - `normalizeTransportQueryScalar(value)`
251
254
 
252
255
  ### `src/shared/validators/jsonApiResponses.js`
@@ -272,18 +275,18 @@ Local functions
272
275
  ### `src/shared/validators/jsonApiRouteTransport.js`
273
276
  Exports
274
277
  - `JSON_API_ERROR_DOCUMENT_SCHEMA`
275
- - `createJsonApiResourceObjectTransportSchema({ type = "", attributes, requireId = true, includeLinks = false, includeMeta = false, excludeAttributeKeys = [], relationshipEntries = [], relationshipMembersRequired = false } = {})`
278
+ - `createJsonApiResourceObjectTransportSchema({ type = "", attributes, requireId = true, includeLinks = false, includeMeta = false, excludeAttributeKeys = [], relationshipEntries = [], relationshipMembersRequired = false, allowSparseFields = false } = {})`
276
279
  - `createJsonApiResourceRequestBodyTransportSchema({ type = "", attributes, requireId = false, excludeAttributeKeys = [], relationshipEntries = [] } = {})`
277
- - `createJsonApiResourceSuccessTransportSchema({ type = "", attributes, kind = "record", includeLinks = false, includeMeta = false, includeIncluded = false, excludeAttributeKeys = [], relationshipEntries = [] } = {})`
280
+ - `createJsonApiResourceSuccessTransportSchema({ type = "", attributes, kind = "record", includeLinks = false, includeMeta = false, includeIncluded = false, allowSparseFields = false, excludeAttributeKeys = [], relationshipEntries = [] } = {})`
278
281
  - `withJsonApiErrorResponses(successResponses, { includeValidation400 = false } = {})`
279
282
  - `createJsonApiResourceRouteTransport({ type = "", requestType = "", responseType = "", query = null, allowBodyId = false, successKind = "record", pointerPrefix = "/data/attributes", mapRequestRelationships = null, getRecordType = null, getRecordId = null, getRecordAttributes = null, getRecordRelationships = null, getRecordLinks = null, getRecordMeta = null, getIncluded = null, getDocumentLinks = null, getDocumentMeta = null, getCollectionItems = null } = {})`
280
- - `createJsonApiResourceRouteContract({ type = "", requestType = "", responseType = "", body = null, query = null, output = null, outputKind = "record", successStatus = 200, includeValidation400 = false, allowBodyId = false, pointerPrefix = "/data/attributes", bodyAttributeExcludeKeys = [], outputAttributeExcludeKeys = [], bodyRelationshipEntries = [], outputRelationshipEntries = [], getRecordType = null, getRecordId = null, getRecordAttributes = null, getRecordRelationships = null, getRecordLinks = null, getRecordMeta = null, getIncluded = null, getDocumentLinks = null, getDocumentMeta = null, getCollectionItems = null, mapRequestRelationships = null } = {})`
283
+ - `createJsonApiResourceRouteContract({ type = "", requestType = "", responseType = "", body = null, query = null, output = null, outputKind = "record", successStatus = 200, includeValidation400 = false, allowSparseFields = false, allowBodyId = false, pointerPrefix = "/data/attributes", bodyAttributeExcludeKeys = [], outputAttributeExcludeKeys = [], bodyRelationshipEntries = [], outputRelationshipEntries = [], getRecordType = null, getRecordId = null, getRecordAttributes = null, getRecordRelationships = null, getRecordLinks = null, getRecordMeta = null, getIncluded = null, getDocumentLinks = null, getDocumentMeta = null, getCollectionItems = null, mapRequestRelationships = null } = {})`
281
284
  Local functions
282
285
  - `isRecord(value)`
283
286
  - `createJsonApiTransportError(statusCode, message, code)`
284
287
  - `resolveRouteType(type = "")`
285
288
  - `resolveRouteTypes(value = {})`
286
- - `resolveEmbeddedAttributesTransportSchema(definition, { context = "JSON:API resource", defaultMode = "replace", removeId = false, removeKeys = [] } = {})`
289
+ - `resolveEmbeddedAttributesTransportSchema(definition, { context = "JSON:API resource", defaultMode = "replace", removeId = false, removeKeys = [], allowSparseFields = false } = {})`
287
290
  - `normalizeRelationshipSchemaEntries(entries = [])`
288
291
  - `createJsonApiRelationshipDataSchema(relationshipType = "", { many = false, nullable = false } = {})`
289
292
  - `createJsonApiRelationshipsTransportSchema(entries = [], { includeRequired = false } = {})`
@@ -31,6 +31,7 @@ Exports
31
31
  - `extractJsonRestCollectionRows(payload = null)`
32
32
  - `isJsonRestResourceMissingError(error = null)`
33
33
  - `returnNullWhenJsonRestResourceMissing(run)`
34
+ - `returnBadRequestWhenJsonRestFieldsetInvalid(run)`
34
35
  - `resolveWorkspaceScopeValue(context = null)`
35
36
  - `resolveUserScopeValue(context = null)`
36
37
  - `createJsonRestApiHost({ knex })`
@@ -47,7 +48,10 @@ Local functions
47
48
  - `normalizeJsonRestQueryField(fieldName = "", fieldDefinition = {}, projectionDefinition = null)`
48
49
  - `isJsonRestVirtualField(fieldDefinition = null)`
49
50
  - `applyJsonRestQueryFields(scopeOptions = {}, extraQueryFields = {})`
51
+ - `resolveJsonRestDefaultExcludedFields(resource = {})`
52
+ - `applyJsonRestDefaultExclusions(scopeOptions = {}, resource = {})`
50
53
  - `extractJsonApiInputRelationships(attributes = {}, resource = null, relationships = null)`
54
+ - `isJsonRestSparseFieldError(error = null)`
51
55
 
52
56
  ### root
53
57
 
@@ -229,6 +229,7 @@ Exports
229
229
  - `parseCrudListRangeQueryExpression(value = null)`
230
230
  - `formatCrudListRangeQueryExpression(startValue = "", endValue = "", { collapseExact = false } = {})`
231
231
  - `defineCrudListFilters(definitions = {})`
232
+ - `createCrudListFilterEmptyValue(filter = {})`
232
233
  - `createCrudListFilterInitialValue(filter = {})`
233
234
  - `isCrudListFilterMultiValue(filter = {})`
234
235
  - `isCrudListFilterStructuredValue(filter = {})`
@@ -336,6 +337,12 @@ Exports
336
337
  - `shouldRetryTransientQueryFailure`
337
338
  - `transientQueryRetryDelay`
338
339
 
340
+ ### `shared/support/jsonApiFieldsets.js`
341
+ Exports
342
+ - `normalizeJsonApiFieldList(value = [])`
343
+ - `normalizeJsonApiFieldsets(value = {}, { primaryType = "" } = {})`
344
+ - `buildJsonApiFieldsetsToken(value = {})`
345
+
339
346
  ### `shared/support/linkPath.js`
340
347
  Exports
341
348
  - `isExternalLinkTarget(target = "")`
@@ -259,11 +259,11 @@ Exports
259
259
 
260
260
  ### `src/client/composables/records/useList.js`
261
261
  Exports
262
- - `useList({ ownershipFilter = ROUTE_VISIBILITY_WORKSPACE, surfaceId = "", access = "auto", apiSuffix = "", queryKeyFactory = null, viewPermissions = [], readEnabled = true, placementSource = "users-web.list", fallbackLoadError = "Unable to load list.", initialPageParam = null, getNextPageParam, selectItems, client = null, transport = null, requestOptions, queryOptions, requestRecovery, requestRecoveryLabel = "List", realtime = null, adapter = null, recordIdParam = "recordId", recordIdSelector = null, viewUrlTemplate = "", editUrlTemplate = "", search = null, queryParams = null, requestQueryParams = null, syncToRoute = false } = {})`
262
+ - `useList({ ownershipFilter = ROUTE_VISIBILITY_WORKSPACE, surfaceId = "", access = "auto", apiSuffix = "", queryKeyFactory = null, viewPermissions = [], readEnabled = true, placementSource = "users-web.list", fallbackLoadError = "Unable to load list.", initialPageParam = null, getNextPageParam, selectItems, client = null, transport = null, requestOptions, queryOptions, requestRecovery, requestRecoveryLabel = "List", realtime = null, adapter = null, recordIdParam = "recordId", recordIdSelector = null, viewUrlTemplate = "", editUrlTemplate = "", search = null, queryParams = null, routeQueryValueResolvers = null, requestQueryParams = null, requestFieldsets = null, syncToRoute = false } = {})`
263
263
 
264
264
  ### `src/client/composables/records/useView.js`
265
265
  Exports
266
- - `useView({ resource = null, ownershipFilter = ROUTE_VISIBILITY_WORKSPACE, surfaceId = "", access = "auto", apiSuffix = "", queryKeyFactory = null, viewPermissions = [], readMethod = "GET", readEnabled = true, client = null, transport = null, requestRecovery = null, requestRecoveryLabel = "Resource", placementSource = "users-web.view", fallbackLoadError = "Unable to load resource.", notFoundStatuses = [404], notFoundMessage = "Record not found.", model, mapLoadedToModel, requestQueryParams = null, recordIdParam = "recordId", routeParams = null, routeRecordId = null, apiUrlTemplate = "", listUrlTemplate = "", editUrlTemplate = "", includeRecordIdInQueryKey = false, realtime = undefined, adapter = null } = {})`
266
+ - `useView({ resource = null, ownershipFilter = ROUTE_VISIBILITY_WORKSPACE, surfaceId = "", access = "auto", apiSuffix = "", queryKeyFactory = null, viewPermissions = [], readMethod = "GET", readEnabled = true, client = null, transport = null, requestRecovery = null, requestRecoveryLabel = "Resource", placementSource = "users-web.view", fallbackLoadError = "Unable to load resource.", notFoundStatuses = [404], notFoundMessage = "Record not found.", model, mapLoadedToModel, requestQueryParams = null, requestFieldsets = null, recordIdParam = "recordId", routeParams = null, routeRecordId = null, apiUrlTemplate = "", listUrlTemplate = "", editUrlTemplate = "", includeRecordIdInQueryKey = false, realtime = undefined, adapter = null } = {})`
267
267
 
268
268
  ### `src/client/composables/runtime/addEditUiRuntime.js`
269
269
  Exports
@@ -360,11 +360,11 @@ Local functions
360
360
  ### `src/client/composables/support/listQueryParamSupport.js`
361
361
  Exports
362
362
  - `normalizeListSyncToRouteConfig(syncToRoute = false, { defaultSearchParam = "q" } = {})`
363
- - `resolveQueryParamDescriptors(queryParams, context = {})`
363
+ - `resolveQueryParamDescriptors(queryParams, context = {}, { routeValueResolvers = null } = {})`
364
364
  - `resolveActiveQueryParamEntries(descriptors = [])`
365
365
  - `resolveWritableQueryParamBindings(descriptors = [])`
366
366
  - `buildQueryParamEntriesToken(entries = [])`
367
- - `parseRouteBindingValue(binding, routeQueryValue)`
367
+ - `parseRouteBindingValue(binding, routeQueryValue, context = {})`
368
368
  - `areQueryParamBindingValuesEqual(left, right)`
369
369
  - `buildRouteQueryCompareToken(query = {})`
370
370
  - `mergeManagedQueryParamKeyHistory(history = [], keys = [])`
@@ -375,7 +375,7 @@ Local functions
375
375
  - `resolveQueryParamsInput(queryParams, context = {})`
376
376
  - `resolveQueryParamBindingType(value)`
377
377
  - `resolveArrayQueryParamItemType(values = [])`
378
- - `createWritableQueryParamBinding({ source = {}, rawKey = "", rawValue = null, key = "" } = {})`
378
+ - `createWritableQueryParamBinding({ source = {}, rawKey = "", rawValue = null, key = "", resolveRouteValue = null } = {})`
379
379
  - `firstRouteQueryValue(value)`
380
380
  - `normalizeRouteQueryValues(value)`
381
381
  - `parseRouteBooleanValue(value, fallback = false)`
@@ -398,11 +398,13 @@ Exports
398
398
 
399
399
  ### `src/client/composables/support/requestQueryRuntimeSupport.js`
400
400
  Exports
401
- - `appendRequestQueryValue(target = {}, key = "", values = [])`
402
- - `buildRequestQueryObject(entries = [])`
403
- - `createRequestQueryRuntime({ requestQueryParams = null, context = null, sourceQueryKey = null } = {})`
404
- - `resolveRequestQueryBaseKey(sourceQueryKey = null)`
401
+ - `buildRequestQueryObject(entries = [], { fieldsets = null } = {})`
402
+ - `createRequestQueryRuntime({ requestQueryParams = null, requestFieldsets = null, context = null, sourceQueryKey = null } = {})`
403
+ Local functions
405
404
  - `resolveRequestQueryContext(context = null)`
405
+ - `resolveRequestQueryBaseKey(sourceQueryKey = null)`
406
+ - `resolveRequestFieldsets(requestFieldsets = null, context = {})`
407
+ - `appendRequestQueryValue(target = {}, key = "", values = [])`
406
408
 
407
409
  ### `src/client/composables/support/resourceLoadStateHelpers.js`
408
410
  Exports
@@ -501,6 +503,8 @@ Local functions
501
503
  - `resetFilterValue(values, filter = {})`
502
504
  - `applyPresetFilterValue(values, filter = {}, rawValue)`
503
505
  - `createQueryParams(values, filterEntries = [])`
506
+ - `resolveFilterRouteValue(filter, routeValue, { initial = false } = {})`
507
+ - `createRouteQueryValueResolvers(filterEntries = [])`
504
508
  - `resolveAtomicValueLabel(filter = {}, value = "", labelResolvers = {})`
505
509
  - `formatDefaultChipLabel(filter = {}, chipValue, labelResolvers = {})`
506
510
 
@@ -521,7 +525,7 @@ Local functions
521
525
 
522
526
  ### `src/client/composables/useCrudListScreen.js`
523
527
  Exports
524
- - `useCrudListScreen({ adapter = null, resource = null, resourceNamespace = "resource", apiSuffix = "", recordIdParam = "recordId", recordIdSelector = null, titleFallbackFieldKey = "", viewUrlTemplate = "", editUrlTemplate = "", newUrlTemplate = "", recordChangedEvents = [], listFilters = {}, listBulkActions = [], listRowActions = [], syntheticRows = null, routeQueryBlacklist = Object.freeze(["include", "cursor", "limit"]), requestQueryParams = null, readEnabled = true, requestRecoveryLabel = "Records", fallbackLoadError = "Unable to load records." } = {})`
528
+ - `useCrudListScreen({ adapter = null, resource = null, resourceNamespace = "resource", apiSuffix = "", recordIdParam = "recordId", recordIdSelector = null, titleFallbackFieldKey = "", viewUrlTemplate = "", editUrlTemplate = "", newUrlTemplate = "", recordChangedEvents = [], listFilters = {}, listBulkActions = [], listRowActions = [], syntheticRows = null, routeQueryBlacklist = Object.freeze(["include", "cursor", "limit"]), requestQueryParams = null, requestFieldsets = null, readEnabled = true, requestRecoveryLabel = "Records", fallbackLoadError = "Unable to load records." } = {})`
525
529
  Local functions
526
530
  - `formatCrudListCardValue(value)`
527
531
  - `asList(value = [])`
@@ -533,7 +537,7 @@ Local functions
533
537
 
534
538
  ### `src/client/composables/useCrudViewScreen.js`
535
539
  Exports
536
- - `useCrudViewScreen({ adapter = null, resource = null, resourceNamespace = "resource", apiUrlTemplate = "", recordIdParam = "recordId", titleFallbackFieldKey = "", listUrlTemplate = "", editUrlTemplate = "", recordChangedEvent = "", requestQueryParams = null, readEnabled = true, queryKeyFactory = null, requestRecoveryLabel = "Record", fallbackLoadError = "Unable to load record.", notFoundMessage = "Record not found." } = {})`
540
+ - `useCrudViewScreen({ adapter = null, resource = null, resourceNamespace = "resource", apiUrlTemplate = "", recordIdParam = "recordId", titleFallbackFieldKey = "", listUrlTemplate = "", editUrlTemplate = "", recordChangedEvent = "", requestQueryParams = null, requestFieldsets = null, readEnabled = true, queryKeyFactory = null, requestRecoveryLabel = "Record", fallbackLoadError = "Unable to load record.", notFoundMessage = "Record not found." } = {})`
537
541
 
538
542
  ### `src/client/composables/usePagedCollection.js`
539
543
  Exports