@jskit-ai/agent-docs 0.1.101 → 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.
@@ -96,6 +96,16 @@ The app has two different migration-related layers:
96
96
 
97
97
  Those are **not** the same step.
98
98
 
99
+ There is also an important ownership distinction:
100
+
101
+ - a CRUD generator owns the installed baseline migration for the table it
102
+ scaffolds
103
+ - the table's app-local package owns later additive schema evolution
104
+
105
+ Never modify or replace a generator-owned baseline migration. Later schema
106
+ evolution must use a new immutable, package-owned additive migration in the
107
+ table's app-local package, declared through `install-migration`.
108
+
99
109
  The npm scripts hide the easy-to-miss first step for normal use. `npm run db:migrate` and `npm run db:migrate:status` run `npm run db:migrations:sync` first, then run Knex. That means package upgrades can add new JSKIT-managed migration files before Knex checks what is pending.
100
110
 
101
111
  ### `jskit migrations ...` writes managed migration files
@@ -157,6 +167,52 @@ So:
157
167
  - sometimes you need only `npm run db:migrate`
158
168
  - sometimes, after repair or re-materialization work, you need **both**
159
169
 
170
+ ### Authoring a later app-owned schema change
171
+
172
+ When an existing CRUD-owned table needs a new column, constraint, index, or
173
+ other compatible evolution, keep the generated baseline unchanged. Ask JSKIT
174
+ to create a new migration source in the app-local package that owns the table:
175
+
176
+ ```bash
177
+ npx jskit create migration \
178
+ --package @local/workflow-record-report-values \
179
+ --id extend-report-value-field-types
180
+ ```
181
+
182
+ This command:
183
+
184
+ 1. verifies that the owner is an installed app-local package
185
+ 2. rejects duplicate or unsafe migration ids
186
+ 3. creates an editable template under the package's
187
+ `templates/migrations/` directory
188
+ 4. adds the matching `install-migration` mutation to the package descriptor
189
+ 5. leaves the migration unmaterialized so its implementation can still be
190
+ completed
191
+
192
+ Implement and test the template first. It intentionally fails if someone tries
193
+ to apply the untouched scaffold. Then materialize and apply it:
194
+
195
+ ```bash
196
+ npx jskit migrations package @local/workflow-record-report-values
197
+ npm run db:migrate
198
+ ```
199
+
200
+ The materialized migration and its lock record are managed artifacts. Once
201
+ installed, the migration id and content are immutable. Any later correction
202
+ must use another additive migration with a new id.
203
+
204
+ SQL inside the source-controlled migration is supported when Knex does not
205
+ express the required schema operation directly. Ad-hoc SQL applied only to a
206
+ development or live database is not a migration and must not be used: it
207
+ creates schema drift, breaks fresh reconstruction, and leaves deployment
208
+ history incomplete.
209
+
210
+ Before completion, exercise the complete migration chain against a fresh
211
+ disposable database as well as the intended upgrade path. A down migration
212
+ must refuse safely when narrowing the schema would invalidate existing data;
213
+ it must never delete or silently transform valuable rows merely to make a
214
+ rollback pass.
215
+
160
216
  ### Shared database helpers
161
217
 
162
218
  The database layer also gives later server code a shared helper surface:
@@ -286,6 +286,25 @@ The exact fields will vary by app. What matters for the generator is:
286
286
  - the column names are stable enough to become part of your app's resource contract
287
287
  - if you are using `crud-server-generator`, do **not** hand-write a separate CRUD migration for this table; the server generator installs the CRUD migration scaffold itself
288
288
 
289
+ That last rule governs the generated baseline, not every future schema change.
290
+ Never modify or replace a generator-owned baseline migration after it has been
291
+ installed. Later schema evolution must use a new immutable, package-owned
292
+ additive migration in the table's app-local package, declared through
293
+ `install-migration`.
294
+
295
+ Create that source and descriptor mutation together with:
296
+
297
+ ```bash
298
+ npx jskit create migration \
299
+ --package @local/contacts \
300
+ --id add-contact-status
301
+ ```
302
+
303
+ Implement the generated template before running
304
+ `npx jskit migrations package @local/contacts`. SQL or Knex schema operations
305
+ inside the source-controlled migration are supported. Running ad-hoc SQL
306
+ against only one database is not, because it creates schema drift.
307
+
289
308
  In this table, `workspace_id` is the important ownership clue. That is why the next step uses:
290
309
 
291
310
  ```bash
@@ -425,6 +444,31 @@ That creates the baseline CRUD route tree:
425
444
  - `w/[workspaceSlug]/admin/contacts/[contactId]/edit.vue`
426
445
  - shared `_components` files under the same route root
427
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
+
428
472
  This is the most important mental model in the whole chapter:
429
473
 
430
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.101",
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
@@ -51,6 +51,9 @@ Rules:
51
51
  - If the table should already be CRUD-owned but should not expose public CRUD HTTP routes yet, scaffold it with `jskit generate crud-server-generator scaffold ... --internal` instead of dropping to direct knex or a hand-built pseudo-repository.
52
52
  - Create the real table directly in the database before scaffolding. `crud-server-generator` reads the live table shape.
53
53
  - If `crud-server-generator` is going to own the CRUD, do not hand-write a separate CRUD migration for that table. The generator installs and manages the CRUD migration scaffold itself.
54
+ - Never modify or replace a generator-owned baseline migration after it has
55
+ been installed. Later schema evolution must use a new immutable,
56
+ package-owned additive migration declared through `install-migration`.
54
57
  - Keep generated table creation in `migrations/` and generated foreign keys in
55
58
  `migrations/constraints/`. The database runtime deliberately runs those
56
59
  phases in that order so valid mutual foreign keys rebuild cleanly without
@@ -72,6 +75,38 @@ Rules:
72
75
  - Structured filters should use shared filter definitions and collapse to compact filter controls/sheets when they outgrow simple search. Do not stack dense desktop filter bars on phone widths.
73
76
  - Use `--navigation-role` for CRUD list placement intent. Main resources can stay `primary`; nested/detail/workflow CRUD routes should usually be `secondary`, `workflow`, or `none`.
74
77
 
78
+ ## Baseline generation versus later schema evolution
79
+
80
+ The initial CRUD scaffold and a later schema change are different operations:
81
+
82
+ - The server generator owns the baseline migration that recreates the table
83
+ from zero. Do not edit, replace, or regenerate that installed baseline to
84
+ express a later change.
85
+ - The table's app-local package owns later schema evolution. Create each change
86
+ as a new immutable additive migration:
87
+
88
+ ```bash
89
+ npx jskit create migration \
90
+ --package @local/workflow-record-report-values \
91
+ --id extend-report-value-field-types
92
+ ```
93
+
94
+ - The authoring command creates an editable migration template and adds its
95
+ `install-migration` mutation to the owning package descriptor in one
96
+ operation. Implement and test the template before materializing it.
97
+ - Materialize the completed source with
98
+ `npx jskit migrations package <package-id>`, then apply it with
99
+ `npm run db:migrate`.
100
+ - SQL or Knex schema operations inside that source-controlled migration are
101
+ supported. Ad-hoc SQL applied only to one database is not: it creates schema
102
+ drift and leaves fresh installations incorrect.
103
+ - Installed migration ids and content are immutable. A correction to an
104
+ installed migration is another additive migration with a new id.
105
+
106
+ A package-owned additive migration is not the prohibited "separate CRUD
107
+ migration." The prohibition applies to competing with or modifying the
108
+ generator-owned baseline.
109
+
75
110
  Meaning of `--internal`:
76
111
 
77
112
  - it keeps the generated repository, service, actions, provider, resource, and CRUD migration ownership chain
@@ -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
@@ -777,6 +777,19 @@ Local functions
777
777
  Exports
778
778
  - `runPackageCreateCommand(ctx = {}, { positional, options, cwd, io })`
779
779
 
780
+ ### `src/server/commandHandlers/packageCommands/createMigration.js`
781
+ Exports
782
+ - `addInstallMigrationMutationToDescriptor(source = "", mutation = {})`
783
+ - `createMigrationTemplate({ packageId, migrationId } = {})`
784
+ - `runMigrationCreateCommand(ctx = {}, { options, cwd, io })`
785
+ Local functions
786
+ - `maskNonCode(source = "")`
787
+ - `findMatchingDelimiter(source, openIndex, openCharacter, closeCharacter)`
788
+ - `findMutationsFilesArray(source = "")`
789
+ - `lineIndentAt(source = "", index = 0)`
790
+ - `renderInstallMigrationMutation({ from, id, indent } = {})`
791
+ - `writeMigrationSourceAndDescriptor({ descriptorPath, descriptorSource, migrationPath, migrationSource, mkdir, rename, rm, writeFile, path } = {})`
792
+
780
793
  ### `src/server/commandHandlers/packageCommands/discoverabilityHelp.js`
781
794
  Exports
782
795
  - `isHelpToken(value = "")`