@jskit-ai/agent-docs 0.1.17 → 0.1.19
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.
- package/guide/agent/app-setup/multi-homing.md +6 -4
- package/guide/agent/app-setup/users.md +11 -4
- package/guide/agent/app-setup/working-with-the-jskit-cli.md +2 -0
- package/guide/agent/generators/crud-generators.md +22 -0
- package/package.json +1 -1
- package/patterns/INDEX.md +3 -0
- package/patterns/client-requests.md +5 -0
- package/patterns/filters.md +4 -0
- package/patterns/server-search.md +211 -0
- package/reference/autogen/packages/auth-provider-supabase-core.md +1 -0
- package/reference/autogen/packages/crud-core.md +8 -0
- package/reference/autogen/packages/crud-server-generator.md +1 -0
- package/reference/autogen/packages/http-runtime.md +12 -6
- package/reference/autogen/packages/json-rest-api-core.md +2 -1
- package/reference/autogen/packages/kernel.md +1 -0
- package/reference/autogen/packages/ui-generator.md +2 -2
- package/reference/autogen/packages/users-web.md +22 -9
- package/reference/autogen/packages/workspaces-web.md +0 -1
- package/reference/autogen/tooling/jskit-cli.md +5 -1
|
@@ -591,12 +591,12 @@ registerProfileSyncLifecycleContributor(app, "workspaces.core.profileSyncLifecyc
|
|
|
591
591
|
return Object.freeze({
|
|
592
592
|
contributorId: "workspaces.core.profileSync",
|
|
593
593
|
order: 100,
|
|
594
|
-
async afterIdentityProfileSynced({ profile,
|
|
595
|
-
if (!
|
|
594
|
+
async afterIdentityProfileSynced({ profile, options } = {}) {
|
|
595
|
+
if (!profile || typeof workspaceService?.ensureProvisionedWorkspaceForAuthenticatedUser !== "function") {
|
|
596
596
|
return;
|
|
597
597
|
}
|
|
598
598
|
|
|
599
|
-
await workspaceService.
|
|
599
|
+
await workspaceService.ensureProvisionedWorkspaceForAuthenticatedUser(profile, options);
|
|
600
600
|
}
|
|
601
601
|
});
|
|
602
602
|
});
|
|
@@ -604,7 +604,9 @@ registerProfileSyncLifecycleContributor(app, "workspaces.core.profileSyncLifecyc
|
|
|
604
604
|
|
|
605
605
|
That means the workspace package does not need to patch auth directly to learn that a user was added. It listens through the `users-core` lifecycle registry instead.
|
|
606
606
|
|
|
607
|
-
For this chapter's `tenancyMode = "personal"` setup, that contributor now does real work. When
|
|
607
|
+
For this chapter's `tenancyMode = "personal"` setup, that contributor now does real work. When an authenticated JSKIT user is synchronized from auth, `workspaces-core` ensures that user's personal workspace exists.
|
|
608
|
+
|
|
609
|
+
That detail matters for the retrofit path described earlier in this chapter. If the app started on `none`, then later switched to `personal`, the first sign-in after that switch still needs to backfill the personal workspace for the already-existing user record. The lifecycle contributor handles that because the workspace provision step is idempotent.
|
|
608
610
|
|
|
609
611
|
- `users-core` owns the "user was synchronized" lifecycle
|
|
610
612
|
- `workspaces-core` subscribes to that lifecycle through the registry
|
|
@@ -212,13 +212,17 @@ The account route itself is very small:
|
|
|
212
212
|
<template>
|
|
213
213
|
<AccountSettingsClientElement />
|
|
214
214
|
</template>
|
|
215
|
+
|
|
216
|
+
<script setup>
|
|
217
|
+
import AccountSettingsClientElement from "@jskit-ai/users-web/client/components/AccountSettingsClientElement";
|
|
218
|
+
</script>
|
|
215
219
|
```
|
|
216
220
|
|
|
217
221
|
That is a very JSKIT-style file.
|
|
218
222
|
|
|
219
223
|
- the route policy is app-owned
|
|
220
224
|
- the page wrapper is app-owned
|
|
221
|
-
- the heavy UI is delegated to a reusable client element
|
|
225
|
+
- the heavy UI is delegated to a package-owned reusable client element
|
|
222
226
|
|
|
223
227
|
So the route is simple, but it is already a real authenticated account screen rather than a placeholder card.
|
|
224
228
|
|
|
@@ -228,18 +232,21 @@ The account page is backed by:
|
|
|
228
232
|
|
|
229
233
|
```text
|
|
230
234
|
src/components/account/settings/
|
|
231
|
-
AccountSettingsClientElement.vue
|
|
232
235
|
AccountSettingsProfileSection.vue
|
|
233
236
|
AccountSettingsPreferencesSection.vue
|
|
234
237
|
AccountSettingsNotificationsSection.vue
|
|
235
238
|
```
|
|
236
239
|
|
|
237
|
-
|
|
240
|
+
Those three section components stay app-owned so you can reshape the actual UI freely.
|
|
241
|
+
|
|
242
|
+
The host itself now lives in `users-web` and resolves every account section through the `account-settings:sections` placement target, including the default `profile`, `preferences`, and `notifications` entries.
|
|
243
|
+
|
|
244
|
+
So the screen follows the same rule as the rest of JSKIT UI: sections are added by placement rather than being hardcoded into an app-owned host component.
|
|
238
245
|
|
|
239
246
|
That is worth noticing because the guide has now reached a new level of scaffolding:
|
|
240
247
|
|
|
241
248
|
- earlier chapters mostly introduced shells and routes
|
|
242
|
-
- this chapter introduces
|
|
249
|
+
- this chapter introduces app-owned leaf section UI while the generic section host stays in the package
|
|
243
250
|
|
|
244
251
|
## Under the hood
|
|
245
252
|
|
|
@@ -568,6 +568,7 @@ So `update` is for reapplying package-owned managed changes, not for generic dep
|
|
|
568
568
|
The best guide example is the tenancy-mode recovery path from the multi-homing chapter. If you installed `workspaces-core` or `workspaces-web` while the app was still on `tenancyMode = "none"`, then later changed the app to `personal`, the missing gated scaffold does not backfill automatically. The recovery path is:
|
|
569
569
|
|
|
570
570
|
```bash
|
|
571
|
+
npx jskit update package users-core
|
|
571
572
|
npx jskit update package workspaces-core
|
|
572
573
|
npx jskit update package workspaces-web
|
|
573
574
|
```
|
|
@@ -665,6 +666,7 @@ In the current CLI, that includes checks such as:
|
|
|
665
666
|
- installed package entries in `.jskit/lock.json`
|
|
666
667
|
- whether managed files recorded in the lock still exist
|
|
667
668
|
- whether installed packages are still visible in the package registry
|
|
669
|
+
- explicit `transport` passed to high-level CRUD hooks such as `useCrudList()`, `useCrudView()`, and `useCrudAddEdit()`, where the shared CRUD resource should derive the JSON:API transport automatically
|
|
668
670
|
- certain JSKIT-specific app checks, such as invalid raw `mdi-*` icon literals in Vue templates when the app uses Vuetify's `mdi-svg` iconset
|
|
669
671
|
- UI verification receipts for current dirty UI files in git, via `.jskit/verification/ui.json`
|
|
670
672
|
|
|
@@ -284,6 +284,28 @@ That file is the shared CRUD contract. The UI generator reads it to decide:
|
|
|
284
284
|
|
|
285
285
|
So even though the server scaffold writes many files, the resource file is the bridge between the server and UI halves.
|
|
286
286
|
|
|
287
|
+
### One install boundary to remember
|
|
288
|
+
|
|
289
|
+
`crud-server-generator scaffold` also adds a new local app package dependency such as:
|
|
290
|
+
|
|
291
|
+
```text
|
|
292
|
+
@local/contacts
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
So before you build or run the app again, install that new local package:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
npm install
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
If you are verifying the guide against a local JSKIT checkout and have already been using local package links, rerun:
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
npm run devlinks
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
The same rule applies after later server scaffolds such as `addresses` and `comments`. The UI generator can still read the generated resource file directly, but the app runtime needs the local package install boundary to be completed before the CRUD can boot normally.
|
|
308
|
+
|
|
287
309
|
For standard CRUDs, that file is now 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.
|
|
288
310
|
|
|
289
311
|
### Step 3: scaffold the UI
|
package/package.json
CHANGED
package/patterns/INDEX.md
CHANGED
|
@@ -28,6 +28,8 @@ How to use it:
|
|
|
28
28
|
- `ui-testing.md`
|
|
29
29
|
- filter, filters, search facets, chips, date range, enum filter, lookup filter, `useCrudListFilters`, `createCrudListFilters`
|
|
30
30
|
- `filters.md`
|
|
31
|
+
- `searchSchema`, `search: true`, `applyFilter`, server search, query validators, backend filters, internal JSON REST filters
|
|
32
|
+
- `server-search.md`
|
|
31
33
|
- `actualField`, `storage.writeSerializer`, `storage.virtual`, computed field, virtual field, projection, `createCrudResourceRuntime`, `remainingBatchWeight`, datetime write serialization
|
|
32
34
|
- `crud-repository-mapping.md`
|
|
33
35
|
|
|
@@ -42,4 +44,5 @@ How to use it:
|
|
|
42
44
|
- [client-requests.md](./client-requests.md)
|
|
43
45
|
- [ui-testing.md](./ui-testing.md)
|
|
44
46
|
- [filters.md](./filters.md)
|
|
47
|
+
- [server-search.md](./server-search.md)
|
|
45
48
|
- [crud-repository-mapping.md](./crud-repository-mapping.md)
|
|
@@ -41,6 +41,11 @@ Use the CRUD wrappers when they fit:
|
|
|
41
41
|
- `useCrudView()` for routed CRUD record loading
|
|
42
42
|
- `useCrudAddEdit()` for routed CRUD forms
|
|
43
43
|
|
|
44
|
+
CRUD hook transport defaults:
|
|
45
|
+
|
|
46
|
+
- CRUD hooks derive the standard JSON:API transport from the shared CRUD `resource` automatically.
|
|
47
|
+
- Do not pass `transport` to CRUD hooks. If you need a non-standard wire contract, drop to `useList()`, `useView()`, `useAddEdit()`, or `usersWebHttpClient.request(...)` instead of the CRUD wrappers.
|
|
48
|
+
|
|
44
49
|
Why this is the standard JSKIT shape:
|
|
45
50
|
|
|
46
51
|
- `useCommand()` resolves the scoped API path for the current route and surface.
|
package/patterns/filters.md
CHANGED
|
@@ -5,6 +5,10 @@ Use when:
|
|
|
5
5
|
- the user asks to "add a filter", "make the list filterable", or "keep filters in the URL"
|
|
6
6
|
- a screen mixes free-text search with structured facets
|
|
7
7
|
|
|
8
|
+
Server-side note:
|
|
9
|
+
- for the current CRUD server contract on the JSON REST path, see `server-search.md`
|
|
10
|
+
- this file is about the shared structured-filter runtime used by CRUD list pages and explicit list-filter modules
|
|
11
|
+
|
|
8
12
|
Ask first:
|
|
9
13
|
- which fields are filterable
|
|
10
14
|
- whether each filter is a flag, enum, multi-enum, date/date-range, number-range, record id, or lookup-backed record id
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Server Search
|
|
2
|
+
|
|
3
|
+
Use when:
|
|
4
|
+
- a CRUD list accepts `q` or other server-side query params
|
|
5
|
+
- a resource needs alias filters such as `onlyArchived -> archived`
|
|
6
|
+
- a list needs access-scoped filters that the repository injects before querying
|
|
7
|
+
- the user asks where search belongs on the server in a current JSKIT app
|
|
8
|
+
|
|
9
|
+
Core rule:
|
|
10
|
+
- for current JSKIT CRUD apps, the server search contract lives in `resource.searchSchema` on the internal JSON REST path
|
|
11
|
+
- route validators decide which public query keys HTTP callers may send
|
|
12
|
+
- repositories may add internal-only filter keys before forwarding the query to JSON REST
|
|
13
|
+
|
|
14
|
+
Fresh generated CRUD repositories already follow this path:
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
return api.resources.contacts.query({
|
|
18
|
+
queryParams: buildJsonRestQueryParams(RESOURCE_TYPE, query),
|
|
19
|
+
simplified: false
|
|
20
|
+
}, createJsonRestContext(context));
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The normal layer split is:
|
|
24
|
+
1. `actions.js` / `registerRoutes.js`: accept and validate public query keys
|
|
25
|
+
2. `repository.js`: add internal-only filter keys such as visibility scope
|
|
26
|
+
3. `*Resource.js`: define the backend filter behavior in `searchSchema`
|
|
27
|
+
|
|
28
|
+
## Decision Rules
|
|
29
|
+
|
|
30
|
+
Use `search: true` on a schema field when:
|
|
31
|
+
- the public filter key should be the same as the field name
|
|
32
|
+
- direct field filtering is enough
|
|
33
|
+
- you are happy with the generated/effective search entry for that one field
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
schema: {
|
|
39
|
+
archived: {
|
|
40
|
+
type: "boolean",
|
|
41
|
+
search: true,
|
|
42
|
+
operations: {
|
|
43
|
+
output: { required: true }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Good fit:
|
|
50
|
+
- `archived=true`
|
|
51
|
+
- `statusSid=active`
|
|
52
|
+
- direct field-by-field filtering with no aliasing
|
|
53
|
+
|
|
54
|
+
Do not use `search: true` when:
|
|
55
|
+
- you want a different public key than the storage field
|
|
56
|
+
- you need one key to search several fields
|
|
57
|
+
- you need custom SQL semantics
|
|
58
|
+
- you need an internal-only injected filter
|
|
59
|
+
|
|
60
|
+
Use explicit `searchSchema` when:
|
|
61
|
+
- the public key differs from the field name
|
|
62
|
+
- one key searches multiple fields
|
|
63
|
+
- the route/API contract should stay stable even if field names change
|
|
64
|
+
- the filter is internal-only and injected by the repository
|
|
65
|
+
- you need to override the simple `search: true` behavior
|
|
66
|
+
|
|
67
|
+
Example:
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
searchSchema: {
|
|
71
|
+
onlyArchived: {
|
|
72
|
+
type: "boolean",
|
|
73
|
+
actualField: "archived",
|
|
74
|
+
filterOperator: "="
|
|
75
|
+
},
|
|
76
|
+
q: {
|
|
77
|
+
type: "string",
|
|
78
|
+
oneOf: ["firstName", "lastName", "email"],
|
|
79
|
+
filterOperator: "like",
|
|
80
|
+
splitBy: " ",
|
|
81
|
+
matchAll: true
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Important merge rule:
|
|
87
|
+
- explicit `searchSchema` is the authored contract
|
|
88
|
+
- fields marked `search: true` are added to the effective search schema too
|
|
89
|
+
- if both define the same public key, explicit `searchSchema` wins
|
|
90
|
+
|
|
91
|
+
Use `applyFilter` when:
|
|
92
|
+
- `actualField`, `oneOf`, and normal operators are not enough
|
|
93
|
+
- the filter needs compound SQL
|
|
94
|
+
- the filter encodes visibility rules, status buckets, or null/not-null semantics that are more complex than a direct field comparison
|
|
95
|
+
|
|
96
|
+
Example:
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
searchSchema: {
|
|
100
|
+
"__viewerAccess": {
|
|
101
|
+
type: "string",
|
|
102
|
+
applyFilter(query, rawValue) {
|
|
103
|
+
const value = String(rawValue || "").trim();
|
|
104
|
+
|
|
105
|
+
query.whereNull("deleted_at");
|
|
106
|
+
if (value === "privileged") {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const userId = value.startsWith("user:")
|
|
111
|
+
? String(value.slice(5) || "").trim()
|
|
112
|
+
: "";
|
|
113
|
+
|
|
114
|
+
query.where(function () {
|
|
115
|
+
this.where("user_added", 0);
|
|
116
|
+
if (userId) {
|
|
117
|
+
this.orWhere("added_by_user_id", userId);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Keep `applyFilter` focused on query semantics:
|
|
126
|
+
- build the SQL/knex filter there
|
|
127
|
+
- do not put permission checks there
|
|
128
|
+
- do not turn it into a second service layer
|
|
129
|
+
|
|
130
|
+
Add route validators when:
|
|
131
|
+
- the filter key is public and may arrive from HTTP query params
|
|
132
|
+
- the page/UI needs a stable, validated query contract
|
|
133
|
+
- malformed values should either be rejected or deliberately discarded
|
|
134
|
+
|
|
135
|
+
Example route layer:
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
input: composeSchemaDefinitions([
|
|
139
|
+
workspaceSlugParamsValidator,
|
|
140
|
+
listCursorPaginationQueryValidator,
|
|
141
|
+
listSearchQueryValidator,
|
|
142
|
+
vetsListFiltersQueryValidator,
|
|
143
|
+
])
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
That means:
|
|
147
|
+
- `q` and the structured filter keys are accepted publicly
|
|
148
|
+
- the repository may still add internal-only keys later
|
|
149
|
+
|
|
150
|
+
Do not add route validators for:
|
|
151
|
+
- repository-injected internal keys such as `"__viewerAccess"`
|
|
152
|
+
- filters that must never be supplied by the client directly
|
|
153
|
+
|
|
154
|
+
## Recommended Shapes
|
|
155
|
+
|
|
156
|
+
Simple direct field exposure:
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
schema: {
|
|
160
|
+
vip: { type: "boolean", search: true }
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Public alias for a real field:
|
|
165
|
+
|
|
166
|
+
```js
|
|
167
|
+
searchSchema: {
|
|
168
|
+
onlyVip: { type: "boolean", actualField: "vip", filterOperator: "=" }
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Grouped text search:
|
|
173
|
+
|
|
174
|
+
```js
|
|
175
|
+
searchSchema: {
|
|
176
|
+
q: {
|
|
177
|
+
type: "string",
|
|
178
|
+
oneOf: ["name", "email", "phone"],
|
|
179
|
+
filterOperator: "like",
|
|
180
|
+
splitBy: " ",
|
|
181
|
+
matchAll: true
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Repository-injected internal filter:
|
|
187
|
+
|
|
188
|
+
```js
|
|
189
|
+
function buildScopedQuery(query = {}, context = null) {
|
|
190
|
+
return {
|
|
191
|
+
...(query || {}),
|
|
192
|
+
"__viewerAccess": resolveViewerAccess(context)
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Keep These Boundaries Clean
|
|
198
|
+
|
|
199
|
+
- Put backend filter behavior in `searchSchema`, not in page code.
|
|
200
|
+
- Put permission checks in the action/service layer, not in `applyFilter`.
|
|
201
|
+
- Put repository-only scoping in the repository, not in route query validation.
|
|
202
|
+
- Do not expose a query key publicly just because the repository uses it internally.
|
|
203
|
+
- Do not rely on route validators alone; a public key still needs a matching backend search definition.
|
|
204
|
+
|
|
205
|
+
## Quick Rule Of Thumb
|
|
206
|
+
|
|
207
|
+
- Same key, same field, simple behavior: `search: true`
|
|
208
|
+
- Public alias or multi-field search: `searchSchema`
|
|
209
|
+
- Complex SQL semantics: `searchSchema` + `applyFilter`
|
|
210
|
+
- Public HTTP query key: add a route validator
|
|
211
|
+
- Internal-only repository key: inject it in `repository.js`, skip the public validator
|
|
@@ -296,6 +296,14 @@ Local functions
|
|
|
296
296
|
### `src/server/routeContracts.js`
|
|
297
297
|
Exports
|
|
298
298
|
- `createCrudJsonApiRouteContracts({ resource = {}, routeParamsValidator = null, listSearchQueryValidator = defaultListSearchQueryValidator, lookupIncludeQueryValidator = defaultLookupIncludeQueryValidator } = {})`
|
|
299
|
+
Local functions
|
|
300
|
+
- `isRecord(value)`
|
|
301
|
+
- `resolveSchemaFieldDefinitions(definition = null)`
|
|
302
|
+
- `resolveJsonApiRelationshipEntries(definition = null)`
|
|
303
|
+
- `createRecordAttributesResolver(definition = null, { excludeKeys = [] } = {})`
|
|
304
|
+
- `createRecordRelationshipsResolver(definition = null)`
|
|
305
|
+
- `createRequestRelationshipMapper(definition = null)`
|
|
306
|
+
- `resolveOutputAttributeExcludeKeys(resource = {})`
|
|
299
307
|
|
|
300
308
|
### `src/server/serviceEvents.js`
|
|
301
309
|
Exports
|
|
@@ -132,6 +132,7 @@ Local functions
|
|
|
132
132
|
- `requireVariableDeclarator(programNode, variableName = "", context = "crud-server-generator scaffold-field")`
|
|
133
133
|
- `requireCrudResourceConfigObject(programNode, context = "crud-server-generator scaffold-field")`
|
|
134
134
|
- `requireResourceSchemaObject(programNode, context = "crud-server-generator scaffold-field")`
|
|
135
|
+
- `assertNoExplicitCrudSchemaOverrides(resourceObject, context = "crud-server-generator scaffold-field")`
|
|
135
136
|
- `findObjectPropertyByName(objectNode, propertyName = "")`
|
|
136
137
|
- `hasObjectProperty(objectNode, propertyName = "")`
|
|
137
138
|
- `insertObjectProperty(objectNode, propertyName = "", valueExpressionSource = "", { context = "crud-server-generator scaffold-field", insertBeforeComputed = false } = {})`
|
|
@@ -111,7 +111,10 @@ Local functions
|
|
|
111
111
|
- `isRecord(value)`
|
|
112
112
|
- `normalizeTransportKind(transport = null)`
|
|
113
113
|
- `defaultEncodeAttributes(body = {})`
|
|
114
|
-
- `
|
|
114
|
+
- `resolveRelationshipFieldKey(relationshipName = "", lookupFieldMap = null)`
|
|
115
|
+
- `createIncludedResourceIndex(document = {})`
|
|
116
|
+
- `simplifyRelationshipResource(linkage = {}, options = {})`
|
|
117
|
+
- `simplifyResourceObject(resource = {}, options = {})`
|
|
115
118
|
- `assertPrimaryDataType(resource = {}, expectedType = "")`
|
|
116
119
|
- `resolveCollectionPageMeta(document = {})`
|
|
117
120
|
|
|
@@ -262,18 +265,21 @@ Local functions
|
|
|
262
265
|
### `src/shared/validators/jsonApiRouteTransport.js`
|
|
263
266
|
Exports
|
|
264
267
|
- `JSON_API_ERROR_DOCUMENT_SCHEMA`
|
|
265
|
-
- `createJsonApiResourceObjectTransportSchema({ type = "", attributes, requireId = true, includeLinks = false, includeMeta = false } = {})`
|
|
266
|
-
- `createJsonApiResourceRequestBodyTransportSchema({ type = "", attributes, requireId = false } = {})`
|
|
267
|
-
- `createJsonApiResourceSuccessTransportSchema({ type = "", attributes, kind = "record", includeLinks = false, includeMeta = false, includeIncluded = false } = {})`
|
|
268
|
+
- `createJsonApiResourceObjectTransportSchema({ type = "", attributes, requireId = true, includeLinks = false, includeMeta = false, excludeAttributeKeys = [], relationshipEntries = [], relationshipMembersRequired = false } = {})`
|
|
269
|
+
- `createJsonApiResourceRequestBodyTransportSchema({ type = "", attributes, requireId = false, excludeAttributeKeys = [], relationshipEntries = [] } = {})`
|
|
270
|
+
- `createJsonApiResourceSuccessTransportSchema({ type = "", attributes, kind = "record", includeLinks = false, includeMeta = false, includeIncluded = false, excludeAttributeKeys = [], relationshipEntries = [] } = {})`
|
|
268
271
|
- `withJsonApiErrorResponses(successResponses, { includeValidation400 = false } = {})`
|
|
269
272
|
- `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 } = {})`
|
|
270
|
-
- `createJsonApiResourceRouteContract({ type = "", requestType = "", responseType = "", body = null, query = null, output = null, outputKind = "record", successStatus = 200, includeValidation400 = false, allowBodyId = false, pointerPrefix = "/data/attributes", getRecordType = null, getRecordId = null, getRecordAttributes = null, getRecordRelationships = null, getRecordLinks = null, getRecordMeta = null, getIncluded = null, getDocumentLinks = null, getDocumentMeta = null, getCollectionItems = null, mapRequestRelationships = null } = {})`
|
|
273
|
+
- `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 } = {})`
|
|
271
274
|
Local functions
|
|
272
275
|
- `isRecord(value)`
|
|
273
276
|
- `createJsonApiTransportError(statusCode, message, code)`
|
|
274
277
|
- `resolveRouteType(type = "")`
|
|
275
278
|
- `resolveRouteTypes(value = {})`
|
|
276
|
-
- `resolveEmbeddedAttributesTransportSchema(definition, { context = "JSON:API resource", defaultMode = "replace", removeId = false } = {})`
|
|
279
|
+
- `resolveEmbeddedAttributesTransportSchema(definition, { context = "JSON:API resource", defaultMode = "replace", removeId = false, removeKeys = [] } = {})`
|
|
280
|
+
- `normalizeRelationshipSchemaEntries(entries = [])`
|
|
281
|
+
- `createJsonApiRelationshipDataSchema(relationshipType = "", { nullable = false } = {})`
|
|
282
|
+
- `createJsonApiRelationshipsTransportSchema(entries = [], { includeRequired = false } = {})`
|
|
277
283
|
- `createJsonApiMetaSuccessTransportSchema({ meta } = {})`
|
|
278
284
|
- `defaultRecordTypeResolver(type)`
|
|
279
285
|
- `defaultRecordIdResolver(record = {})`
|
|
@@ -24,7 +24,7 @@ Exports
|
|
|
24
24
|
- `JSON_REST_AUTOFILTER_PRESETS`
|
|
25
25
|
- `addResourceIfMissing(api, scopeName, resourceConfig)`
|
|
26
26
|
- `buildJsonRestQueryParams(resourceType = "", query = {}, { include = undefined } = {})`
|
|
27
|
-
- `createJsonApiInputRecord(resourceType = "", attributes = {}, { id = null, relationships = null } = {})`
|
|
27
|
+
- `createJsonApiInputRecord(resourceType = "", attributes = {}, { id = null, relationships = null, resource = null } = {})`
|
|
28
28
|
- `createJsonApiRelationship(resourceType = "", id = null)`
|
|
29
29
|
- `createJsonRestResourceScopeOptions(resource = {}, { writeSerializers = {}, normalizeId = null } = {})`
|
|
30
30
|
- `createJsonRestContext(context = null)`
|
|
@@ -42,6 +42,7 @@ Local functions
|
|
|
42
42
|
- `normalizeJsonRestText(value, { fallback = "" } = {})`
|
|
43
43
|
- `normalizeJsonRestObject(value)`
|
|
44
44
|
- `normalizeJsonRestList(value)`
|
|
45
|
+
- `extractJsonApiInputRelationships(attributes = {}, resource = null, relationships = null)`
|
|
45
46
|
- `normalizeJsonApiResourceObject(resource = {})`
|
|
46
47
|
- `buildJsonApiIncludedIndex(payload = {})`
|
|
47
48
|
- `simplifyJsonApiRelationshipData(data, { includedIndex = null, seen = null } = {})`
|
|
@@ -878,6 +878,7 @@ Exports
|
|
|
878
878
|
- `registerRoutes(fastify, { routes = [], app = null, applyRoutePolicy = defaultApplyRoutePolicy, missingHandler = defaultMissingHandler, enableRequestScope = true, requestScopeProperty = "scope", requestActionExecutorProperty = "executeAction", actionExecutorToken = "actionExecutor", requestActionDefaultChannel = "api", requestActionDefaultSurface = "", requestScopeIdPrefix = "http", requestIdResolver = null, middleware = {} } = {})`
|
|
879
879
|
Local functions
|
|
880
880
|
- `toFastifyRouteOptions(route)`
|
|
881
|
+
- `shouldStripFastifyBodySchema(route = null, schema = null)`
|
|
881
882
|
- `normalizeHeaderValue(value)`
|
|
882
883
|
- `normalizeMediaType(value = "")`
|
|
883
884
|
- `routeDefinesRequestBody(route = null)`
|
|
@@ -54,8 +54,8 @@ Exports
|
|
|
54
54
|
- `DEFAULT_SUBPAGES_POSITION`
|
|
55
55
|
- `DEFAULT_COMPONENT_DIRECTORY`
|
|
56
56
|
- `SECTION_CONTAINER_SHELL_COMPONENT`
|
|
57
|
-
- `
|
|
58
|
-
- `
|
|
57
|
+
- `SUBPAGES_LINK_COMPONENT`
|
|
58
|
+
- `SUBPAGES_LINK_COMPONENT_TOKEN`
|
|
59
59
|
- `resolvePageTargetDetails`
|
|
60
60
|
- `resolveNearestParentSubpagesHost`
|
|
61
61
|
- `deriveDefaultSubpagesHost`
|
|
@@ -18,7 +18,6 @@ Use this on demand; do not load the full index at startup.
|
|
|
18
18
|
Exports
|
|
19
19
|
- `ACCOUNT_SETTINGS_SECTION_TARGET`
|
|
20
20
|
- `EMPTY_ACCOUNT_SETTINGS_SECTIONS`
|
|
21
|
-
- `RESERVED_ACCOUNT_SETTINGS_SECTION_VALUES`
|
|
22
21
|
- `normalizeAccountSettingsSectionEntry(entry = null)`
|
|
23
22
|
- `resolveAccountSettingsSections(entries = [])`
|
|
24
23
|
- `sortAccountSettingsSections(entries = [])`
|
|
@@ -29,6 +28,13 @@ Exports
|
|
|
29
28
|
- `createUsersBootstrapUserHandler()`
|
|
30
29
|
- `registerUsersBootstrapPayloadHandlers(app)`
|
|
31
30
|
|
|
31
|
+
### `src/client/components/AccountSettingsClientElement.vue`
|
|
32
|
+
Exports
|
|
33
|
+
- None
|
|
34
|
+
Local functions
|
|
35
|
+
- `normalizeSection(value)`
|
|
36
|
+
- `readRouteSection()`
|
|
37
|
+
|
|
32
38
|
### `src/client/components/ProfileClientElement.vue`
|
|
33
39
|
Exports
|
|
34
40
|
- None
|
|
@@ -75,6 +81,17 @@ Exports
|
|
|
75
81
|
- `resolveCrudBindingValues(values, context = {})`
|
|
76
82
|
- `resolveCrudBoundValues({ binding = {}, routeValues = {}, context = {} } = {})`
|
|
77
83
|
|
|
84
|
+
### `src/client/composables/crud/crudJsonApiTransportSupport.js`
|
|
85
|
+
Exports
|
|
86
|
+
- `inferCrudJsonApiTransport(resource = null, { mode = "", operationName = "" } = {})`
|
|
87
|
+
- `resolveCrudJsonApiTransport(transport = null, resource = null, options = {})`
|
|
88
|
+
- `resolveLookupFieldMap(resource = null)`
|
|
89
|
+
Local functions
|
|
90
|
+
- `isRecord(value)`
|
|
91
|
+
- `resolveSchemaFieldDefinitions(definition = null)`
|
|
92
|
+
- `isJsonApiResourceTransport(transport = null)`
|
|
93
|
+
- `resolveCrudJsonApiResourceType(resource = null)`
|
|
94
|
+
|
|
78
95
|
### `src/client/composables/crud/crudLookupFieldLabelSupport.js`
|
|
79
96
|
Exports
|
|
80
97
|
- `resolveLookupItemLabel(item = {}, labelKey = "")`
|
|
@@ -84,6 +101,7 @@ Local functions
|
|
|
84
101
|
- `hasDisplayValue(value)`
|
|
85
102
|
- `resolveComposedLabel(source = {}, candidates = LOOKUP_LABEL_COMPOSITION_CANDIDATES)`
|
|
86
103
|
- `resolveLookupFieldDescriptor(field = {}, relationKind = "", valueKey = "", labelKey = "")`
|
|
104
|
+
- `resolveFallbackLookupKey(key = "")`
|
|
87
105
|
|
|
88
106
|
### `src/client/composables/crud/crudLookupFieldRuntime.js`
|
|
89
107
|
Exports
|
|
@@ -108,6 +126,7 @@ Local functions
|
|
|
108
126
|
- `resolveFormFieldType(field = {})`
|
|
109
127
|
- `resolveFormFieldFormat(field = {})`
|
|
110
128
|
- `isNullableFormField(field = {})`
|
|
129
|
+
- `isLookupFormField(field = {})`
|
|
111
130
|
- `padDateTimePart(value)`
|
|
112
131
|
- `normalizeTimeWhitespace(value)`
|
|
113
132
|
- `toTimeInputValue(value)`
|
|
@@ -166,7 +185,7 @@ Local functions
|
|
|
166
185
|
|
|
167
186
|
### `src/client/composables/records/useCrudView.js`
|
|
168
187
|
Exports
|
|
169
|
-
- `useCrudView({ paramBinding = null, route = null, ...viewOptions } = {})`
|
|
188
|
+
- `useCrudView({ resource = null, paramBinding = null, route = null, ...viewOptions } = {})`
|
|
170
189
|
|
|
171
190
|
### `src/client/composables/records/useList.js`
|
|
172
191
|
Exports
|
|
@@ -433,6 +452,7 @@ Exports
|
|
|
433
452
|
### `src/client/index.js`
|
|
434
453
|
Exports
|
|
435
454
|
- `UsersWebClientProvider`
|
|
455
|
+
- `AccountSettingsClientElement`
|
|
436
456
|
- `clientProviders`
|
|
437
457
|
|
|
438
458
|
### `src/client/lib/bootstrap.js`
|
|
@@ -486,13 +506,6 @@ Exports
|
|
|
486
506
|
|
|
487
507
|
### templates
|
|
488
508
|
|
|
489
|
-
### `templates/src/components/account/settings/AccountSettingsClientElement.vue`
|
|
490
|
-
Exports
|
|
491
|
-
- None
|
|
492
|
-
Local functions
|
|
493
|
-
- `normalizeSection(value)`
|
|
494
|
-
- `readRouteSection()`
|
|
495
|
-
|
|
496
509
|
### `templates/src/components/account/settings/AccountSettingsNotificationsSection.vue`
|
|
497
510
|
Exports
|
|
498
511
|
- None
|
|
@@ -333,7 +333,6 @@ Local functions
|
|
|
333
333
|
- `normalizePendingInvitesCount(value)`
|
|
334
334
|
- `resolveReturnTo()`
|
|
335
335
|
- `resolveReturnToHref()`
|
|
336
|
-
- `countPendingInvites(entries = [])`
|
|
337
336
|
|
|
338
337
|
### `templates/packages/main/src/client/components/AccountSettingsInvitesSection.vue`
|
|
339
338
|
Exports
|
|
@@ -166,7 +166,7 @@ Exports
|
|
|
166
166
|
|
|
167
167
|
### `src/server/cliRuntime/mutations/fileMutations.js`
|
|
168
168
|
Exports
|
|
169
|
-
- `applyFileMutations(packageEntry, appRoot, preparedMutations, managedFiles, managedMigrations, touchedFiles, warnings = [], existingManagedFiles = [])`
|
|
169
|
+
- `applyFileMutations(packageEntry, appRoot, preparedMutations, managedFiles, managedMigrations, touchedFiles, warnings = [], existingManagedFiles = [], { reapplyManagedAppFiles = false } = {})`
|
|
170
170
|
- `prepareFileMutations(packageEntry, options, appRoot, fileMutations, existingManagedFiles = [])`
|
|
171
171
|
|
|
172
172
|
### `src/server/cliRuntime/mutations/installMigrationMutation.js`
|
|
@@ -368,6 +368,10 @@ Local functions
|
|
|
368
368
|
### `src/server/commandHandlers/appCommands/linkLocalPackages.js`
|
|
369
369
|
Exports
|
|
370
370
|
- `runAppLinkLocalPackagesCommand(ctx = {}, { appRoot = "", options = {}, stdout })`
|
|
371
|
+
Local functions
|
|
372
|
+
- `collectDeclaredPackageNames(packageJson = {})`
|
|
373
|
+
- `verifySymlinkTarget(targetPath = "", sourceDir = "", { packageName = "" } = {})`
|
|
374
|
+
- `maybeLinkCompanionPackages({ appRoot = "", repoRoot = "", stdout, createCliError })`
|
|
371
375
|
|
|
372
376
|
### `src/server/commandHandlers/appCommands/release.js`
|
|
373
377
|
Exports
|