@jskit-ai/agent-docs 0.1.19 → 0.1.20
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/initial-scaffolding.md +12 -8
- package/guide/agent/app-setup/multi-homing.md +59 -0
- package/guide/agent/app-setup/users.md +8 -0
- package/guide/agent/generators/crud-generators.md +210 -62
- package/guide/agent/generators/ui-generators.md +31 -0
- package/package.json +1 -1
- package/patterns/page-redirects.md +5 -1
- package/reference/autogen/README.md +1 -0
- package/reference/autogen/packages/assistant-core.md +13 -0
- package/reference/autogen/packages/assistant-runtime.md +2 -0
- package/reference/autogen/packages/console-core.md +2 -0
- package/reference/autogen/packages/crud-core.md +4 -0
- package/reference/autogen/packages/crud-ui-generator.md +3 -0
- package/reference/autogen/packages/feature-server-generator.md +74 -0
- package/reference/autogen/packages/json-rest-api-core.md +1 -5
- package/reference/autogen/packages/users-web.md +1 -0
- package/reference/autogen/tooling/create-app.md +4 -16
- package/reference/autogen/tooling/jskit-cli.md +10 -0
|
@@ -630,13 +630,11 @@ export default Object.freeze({
|
|
|
630
630
|
kind: "runtime",
|
|
631
631
|
runtime: {
|
|
632
632
|
server: {
|
|
633
|
-
providerEntrypoint: "src/server/
|
|
633
|
+
providerEntrypoint: "src/server/MainServiceProvider.js",
|
|
634
634
|
providers: [
|
|
635
635
|
{
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
pattern: "*Provider.js"
|
|
639
|
-
}
|
|
636
|
+
entrypoint: "src/server/MainServiceProvider.js",
|
|
637
|
+
export: "MainServiceProvider"
|
|
640
638
|
}
|
|
641
639
|
]
|
|
642
640
|
}
|
|
@@ -651,12 +649,12 @@ export default Object.freeze({
|
|
|
651
649
|
|
|
652
650
|
This is the moment where the scaffold stops looking like "just a Vue app". The app is declaring itself as a runtime package that JSKIT can discover, load, and mutate safely.
|
|
653
651
|
|
|
654
|
-
For the server side, the main file to remember is `packages/main/src/server/
|
|
652
|
+
For the server side, the main file to remember is `packages/main/src/server/MainServiceProvider.js`. It stays intentionally flat and small. That is the point: `packages/main` is the app-local composition package, not the default home for new backend feature trees.
|
|
655
653
|
|
|
656
654
|
The server-side provider starts like this:
|
|
657
655
|
|
|
658
656
|
```js
|
|
659
|
-
import { loadAppConfig } from "
|
|
657
|
+
import { loadAppConfig } from "./loadAppConfig.js";
|
|
660
658
|
|
|
661
659
|
class MainServiceProvider {
|
|
662
660
|
static id = "local.main";
|
|
@@ -674,7 +672,13 @@ class MainServiceProvider {
|
|
|
674
672
|
export { MainServiceProvider };
|
|
675
673
|
```
|
|
676
674
|
|
|
677
|
-
It is deliberately small
|
|
675
|
+
It is deliberately small because it is only for app-local glue: loading config, wiring tiny app-specific behavior, and bootstrapping shared runtime concerns. When a backend capability becomes substantial, do not grow `packages/main` into a mini service tree. Generate a dedicated package instead:
|
|
676
|
+
|
|
677
|
+
```bash
|
|
678
|
+
npx jskit generate feature-server-generator scaffold booking-engine
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
That keeps the ownership boundary clear: `packages/main` stays composition-only, while real server features get their own provider, service, and optional repository seams. The client side uses the same provider lifecycle; you already saw the matching pattern earlier in the client boot path.
|
|
678
682
|
|
|
679
683
|
The `.jskit/lock.json` file is also important. Treat it like JSKIT's own lock and state file. It records which runtime packages JSKIT believes are installed and which managed changes they introduced. When you use `jskit add`, `jskit update`, or generators that depend on installed package state, this file is part of the source of truth. It belongs in version control, and you should not hand-edit it.
|
|
680
684
|
|
|
@@ -47,6 +47,12 @@ npm install
|
|
|
47
47
|
npm run db:migrate
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
**Important: This Block Is Only The Fresh Workspace Install Path**
|
|
51
|
+
|
|
52
|
+
These commands are complete only if the app was already on `tenancyMode = "personal"` when `users-web` / `users-core` were originally installed, or if you already ran the recovery `npx jskit update package users-core` step above.
|
|
53
|
+
|
|
54
|
+
If you changed tenancy after installing users, do not skip that update. The workspace packages add workspace runtime and routes, but `users-core` is what rewrites the app-local users scaffold into its workspace-aware shape.
|
|
55
|
+
|
|
50
56
|
`workspaces-core` adds the server-side workspace runtime and schema migrations. `workspaces-web` adds the workspace-facing client surfaces, shell placements, and app-owned route files.
|
|
51
57
|
|
|
52
58
|
If you want to inspect that package before installing it, this is a very good moment to use the CLI chapter's inspection command:
|
|
@@ -306,6 +312,30 @@ Concretely, that route tree works like this:
|
|
|
306
312
|
- `w/[workspaceSlug]/admin/workspace/settings.vue` is a section shell. It does not own the actual settings fields. It owns the card frame, the left-side settings menu outlet, and the nested `<RouterView />` where child settings pages render.
|
|
307
313
|
- `w/[workspaceSlug]/admin/workspace/settings/index.vue` is intentionally almost empty. Its job is to make `/admin/workspace/settings` a real route today and give you a clean place to redirect or add child settings pages later.
|
|
308
314
|
|
|
315
|
+
If you want to add a real workspace settings child page at this point, use the normal page generator under that route tree:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
npx jskit generate ui-generator page \
|
|
319
|
+
w/[workspaceSlug]/admin/workspace/settings/billing/index.vue \
|
|
320
|
+
--name "Billing"
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
That command does two things:
|
|
324
|
+
|
|
325
|
+
- it creates `src/pages/w/[workspaceSlug]/admin/workspace/settings/billing/index.vue`
|
|
326
|
+
- it also appends the matching workspace settings menu entry into `src/placement.js`
|
|
327
|
+
|
|
328
|
+
The reason JSKIT can wire that link automatically is that the workspace settings shell already exposes a named placement outlet with a default link renderer:
|
|
329
|
+
|
|
330
|
+
```vue
|
|
331
|
+
<ShellOutlet
|
|
332
|
+
target="admin-settings:primary-menu"
|
|
333
|
+
default-link-component-token="local.main.ui.surface-aware-menu-link-item"
|
|
334
|
+
/>
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
`target="admin-settings:primary-menu"` tells the generator which settings menu host owns those child links. `default-link-component-token="local.main.ui.surface-aware-menu-link-item"` tells it which local menu-link component to use unless you override it. So a page generated under `w/[workspaceSlug]/admin/workspace/settings/...` automatically lands in the left-side workspace settings menu without you hand-writing the placement entry.
|
|
338
|
+
|
|
309
339
|
### Workspace pages are prepared for missing-workspace states
|
|
310
340
|
|
|
311
341
|
The starter workspace pages already use a dedicated unavailable-state helper:
|
|
@@ -558,6 +588,35 @@ That one block explains a lot of the new shell behavior.
|
|
|
558
588
|
- the admin surface gets workspace tools in the top-right area
|
|
559
589
|
- the admin workspace settings menu is now another nested placement host with both `Settings` and `Members`
|
|
560
590
|
|
|
591
|
+
If you want to add your own app-owned page into that top cog menu, first ask JSKIT which placement targets exist:
|
|
592
|
+
|
|
593
|
+
```bash
|
|
594
|
+
npx jskit list-placements
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
In a workspace-enabled app, that list now includes:
|
|
598
|
+
|
|
599
|
+
```text
|
|
600
|
+
- admin-cog:primary-menu [package:@jskit-ai/workspaces-web:src/client/components/UsersWorkspaceToolsWidget.vue]
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
If you want more context than the raw target list, `npx jskit show @jskit-ai/workspaces-web --details` also shows that same outlet plus the default `Settings` and `Members` entries already targeting it.
|
|
604
|
+
|
|
605
|
+
Once you know the outlet id, generate the page like this:
|
|
606
|
+
|
|
607
|
+
```bash
|
|
608
|
+
npx jskit generate ui-generator page \
|
|
609
|
+
w/[workspaceSlug]/admin/catalogue/index.vue \
|
|
610
|
+
--name "Catalogue" \
|
|
611
|
+
--link-placement admin-cog:primary-menu
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
That command creates `src/pages/w/[workspaceSlug]/admin/catalogue/index.vue` and appends the matching link entry into `src/placement.js`.
|
|
615
|
+
|
|
616
|
+
`--link-placement` is necessary here because this route is just a normal `admin` page. It is **not** a child page under a local host like `w/[workspaceSlug]/admin/workspace/settings.vue`, so the generator has no nested settings outlet to infer automatically. If you omit `--link-placement`, the new page link falls back to the app's default shell menu outlet instead of the cog menu.
|
|
617
|
+
|
|
618
|
+
You also do **not** need `--link-component-token` here. `admin-cog:primary-menu` already declares `local.main.ui.surface-aware-menu-link-item` as its default link renderer, so JSKIT reuses that automatically when it writes the placement entry.
|
|
619
|
+
|
|
561
620
|
So the placement system from the shell chapter is still doing the same job as before. The app just has a richer routing and tenancy context now.
|
|
562
621
|
|
|
563
622
|
### The local client provider gets one more app-owned token
|
|
@@ -343,6 +343,14 @@ Under the hood, `users-core` wires those contributors into the users-backed prof
|
|
|
343
343
|
|
|
344
344
|
The next chapter uses exactly that pattern. `workspaces-core` registers a contributor so the workspace layer can react when a new user enters the system.
|
|
345
345
|
|
|
346
|
+
One forward-looking warning matters here. If this app later changes from `tenancyMode = "none"` to `personal` or `workspaces`, the app-local users scaffold written by `users-core` also needs to be refreshed. The multi-homing chapter calls out that recovery step explicitly with:
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
npx jskit update package users-core
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
That is not only a workspace-package concern. The generated `packages/users/...` scaffold itself changes shape when tenancy becomes workspace-aware.
|
|
353
|
+
|
|
346
354
|
## Summary
|
|
347
355
|
|
|
348
356
|
This chapter is where the app stopped treating signed-in people as only Supabase identities and started treating them as real JSKIT users.
|
|
@@ -16,8 +16,8 @@ So the real order is always:
|
|
|
16
16
|
This chapter uses three examples, in increasing complexity:
|
|
17
17
|
|
|
18
18
|
- `contacts`: the baseline full CRUD walkthrough
|
|
19
|
-
- `addresses`: a child CRUD with its own routed list page
|
|
20
|
-
- `comments`: a child CRUD that lives
|
|
19
|
+
- `addresses`: a child CRUD with its own routed list page reached from the parent record view
|
|
20
|
+
- `comments`: a child CRUD that lives under the parent record host as a child page
|
|
21
21
|
|
|
22
22
|
The examples assume the guide app already has the database and workspace/admin setup from the earlier chapters. That is why the route roots below live under `w/[workspaceSlug]/admin/...`.
|
|
23
23
|
|
|
@@ -350,7 +350,7 @@ That is the shape to learn first.
|
|
|
350
350
|
|
|
351
351
|
Now move to a child CRUD with its own URL.
|
|
352
352
|
|
|
353
|
-
This is the right pattern when the child collection deserves its own list page.
|
|
353
|
+
This is the right pattern when the child collection deserves its own list page, but should still be reached from a specific parent record.
|
|
354
354
|
|
|
355
355
|
Example route:
|
|
356
356
|
|
|
@@ -358,12 +358,23 @@ Example route:
|
|
|
358
358
|
.../contacts/3/addresses
|
|
359
359
|
```
|
|
360
360
|
|
|
361
|
-
In the guide app's real route tree,
|
|
361
|
+
In the guide app's real route tree, the visible URL becomes:
|
|
362
362
|
|
|
363
363
|
```text
|
|
364
364
|
w/[workspaceSlug]/admin/contacts/[contactId]/addresses
|
|
365
365
|
```
|
|
366
366
|
|
|
367
|
+
This example is intentionally **not** an in-page child list and **not** a contact subtab.
|
|
368
|
+
|
|
369
|
+
The intended UX is:
|
|
370
|
+
|
|
371
|
+
- `addresses` gets its own routed list page
|
|
372
|
+
- the page lives under a specific contact
|
|
373
|
+
- the user reaches it from an explicit link or button on the contact view
|
|
374
|
+
- it does **not** appear in the global left menu
|
|
375
|
+
|
|
376
|
+
That makes it different from the later `comments` example, where the child list stays inside the parent page.
|
|
377
|
+
|
|
367
378
|
### Step 1: create the child table
|
|
368
379
|
|
|
369
380
|
Example:
|
|
@@ -382,12 +393,16 @@ CREATE TABLE addresses (
|
|
|
382
393
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
383
394
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
384
395
|
KEY idx_addresses_workspace_id (workspace_id),
|
|
385
|
-
KEY idx_addresses_contact_id (contact_id)
|
|
396
|
+
KEY idx_addresses_contact_id (contact_id),
|
|
397
|
+
CONSTRAINT fk_addresses_contact_id
|
|
398
|
+
FOREIGN KEY (contact_id) REFERENCES contacts(id)
|
|
386
399
|
);
|
|
387
400
|
```
|
|
388
401
|
|
|
389
402
|
The important extra column here is `contact_id`. This is what makes the CRUD a child of a contact instead of a top-level resource.
|
|
390
403
|
|
|
404
|
+
The foreign key is not optional in this example. It is what lets `crud-server-generator` emit `contactId` as a real lookup relation in `packages/addresses/src/shared/addressResource.js`.
|
|
405
|
+
|
|
391
406
|
### Step 2: scaffold the server package
|
|
392
407
|
|
|
393
408
|
```bash
|
|
@@ -398,17 +413,104 @@ npx jskit generate crud-server-generator scaffold \
|
|
|
398
413
|
--table-name addresses
|
|
399
414
|
```
|
|
400
415
|
|
|
401
|
-
|
|
416
|
+
Then install the generated local package:
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
npm install
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
If you are developing against a local JSKIT checkout, relink your app to the local packages before you run the UI:
|
|
423
|
+
|
|
424
|
+
```bash
|
|
425
|
+
npm run devlinks
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Step 3: refine the generated lookup metadata by hand
|
|
429
|
+
|
|
430
|
+
Open `packages/addresses/src/shared/addressResource.js` and add `labelKey: "fullName"` to the generated `contactId` relation:
|
|
431
|
+
|
|
432
|
+
```js
|
|
433
|
+
contactId: {
|
|
434
|
+
type: "id",
|
|
435
|
+
required: true,
|
|
436
|
+
search: true,
|
|
437
|
+
relation: {
|
|
438
|
+
kind: "lookup",
|
|
439
|
+
namespace: "contacts",
|
|
440
|
+
valueKey: "id",
|
|
441
|
+
labelKey: "fullName"
|
|
442
|
+
},
|
|
443
|
+
belongsTo: "contacts",
|
|
444
|
+
as: "contact",
|
|
445
|
+
ui: { formControl: "autocomplete" },
|
|
446
|
+
...
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
This part is still manual today.
|
|
451
|
+
|
|
452
|
+
The scaffold can infer the lookup **relationship** from the foreign key, but it cannot infer which contact field should be used as the human-readable label. Without `labelKey: "fullName"`, the page falls back to generic headings like `Addresses for Contact #1`.
|
|
453
|
+
|
|
454
|
+
### Step 4: scaffold the UI route tree
|
|
402
455
|
|
|
403
456
|
```bash
|
|
404
457
|
npx jskit generate crud-ui-generator crud \
|
|
405
458
|
w/[workspaceSlug]/admin/contacts/[contactId]/addresses \
|
|
406
459
|
--resource-file packages/addresses/src/shared/addressResource.js \
|
|
407
460
|
--id-param addressId \
|
|
461
|
+
--parent-title contextual \
|
|
408
462
|
--display-fields label,line1,postcode
|
|
409
463
|
```
|
|
410
464
|
|
|
411
|
-
That gives you a
|
|
465
|
+
That gives you a normal child route tree:
|
|
466
|
+
|
|
467
|
+
- `w/[workspaceSlug]/admin/contacts/[contactId]/addresses/index.vue`
|
|
468
|
+
- `w/[workspaceSlug]/admin/contacts/[contactId]/addresses/new.vue`
|
|
469
|
+
- `w/[workspaceSlug]/admin/contacts/[contactId]/addresses/[addressId]/index.vue`
|
|
470
|
+
- `w/[workspaceSlug]/admin/contacts/[contactId]/addresses/[addressId]/edit.vue`
|
|
471
|
+
- shared `_components` files under the same route root
|
|
472
|
+
|
|
473
|
+
### Step 5: remove the generated shell placement by hand
|
|
474
|
+
|
|
475
|
+
`crud-ui-generator` currently generates a list-page placement block in `src/placement.js`.
|
|
476
|
+
|
|
477
|
+
For this example, that block is **not** what you want.
|
|
478
|
+
|
|
479
|
+
Delete the generated `addresses` placement block from `src/placement.js`.
|
|
480
|
+
|
|
481
|
+
It will be the block added for the `w/[workspaceSlug]/admin/contacts/[contactId]/addresses` page link.
|
|
482
|
+
|
|
483
|
+
Depending on what outlets already exist in the app, that block may try to place `Addresses` in the shell menu or in the contact subpages outlet. For this example, remove it either way and keep navigation manual from the contact view.
|
|
484
|
+
|
|
485
|
+
This is an intentional manual cleanup for now:
|
|
486
|
+
|
|
487
|
+
- keep the routed pages
|
|
488
|
+
- remove the auto menu link
|
|
489
|
+
- navigate to the page from the contact view instead
|
|
490
|
+
|
|
491
|
+
### Step 6: add a manual link from the contact view
|
|
492
|
+
|
|
493
|
+
Open `src/pages/w/[workspaceSlug]/admin/contacts/[contactId]/index.vue` and add a button that links to `./addresses`.
|
|
494
|
+
|
|
495
|
+
The simplest version is:
|
|
496
|
+
|
|
497
|
+
```vue
|
|
498
|
+
<v-btn
|
|
499
|
+
color="primary"
|
|
500
|
+
variant="tonal"
|
|
501
|
+
:to="{ path: view.resolveParams(UI_ADDRESSES_URL), query: $route.query }"
|
|
502
|
+
>
|
|
503
|
+
Addresses
|
|
504
|
+
</v-btn>
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
Then define the URL template in the script:
|
|
508
|
+
|
|
509
|
+
```js
|
|
510
|
+
const UI_ADDRESSES_URL = "./addresses";
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
That gives you a standalone child page with a clear entry point from the parent contact view, without mixing record-scoped pages into the global shell menu.
|
|
412
514
|
|
|
413
515
|
### The important refinement: parent title handling
|
|
414
516
|
|
|
@@ -416,8 +518,8 @@ This is where nested CRUD starts to feel real.
|
|
|
416
518
|
|
|
417
519
|
An addresses list page should not show a generic heading like `Addresses`. It should usually show whose addresses these are, for example:
|
|
418
520
|
|
|
419
|
-
- `Jane Smith
|
|
420
|
-
- `Acme Pty Ltd
|
|
521
|
+
- `Addresses for Jane Smith`
|
|
522
|
+
- `Addresses for Acme Pty Ltd`
|
|
421
523
|
|
|
422
524
|
The awkward case is when there are **no child rows yet**. If the list is empty, you cannot derive the parent title from the first address record.
|
|
423
525
|
|
|
@@ -433,37 +535,36 @@ const parentTitle = useCrudListParentTitle({
|
|
|
433
535
|
});
|
|
434
536
|
```
|
|
435
537
|
|
|
436
|
-
That helper does the
|
|
538
|
+
That helper does the right thing automatically:
|
|
437
539
|
|
|
438
|
-
- if the child list already has rows, it derives the parent title from the first child record
|
|
540
|
+
- if the child list already has rows, it derives the parent title from the first child record when lookup data is available
|
|
439
541
|
- if the child list is empty, it loads the parent record directly
|
|
440
542
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
So the `addresses` example is important because it teaches the first truly practical nested-CRUD problem:
|
|
543
|
+
So the `addresses` example is important because it teaches the first truly practical child-page problem:
|
|
444
544
|
|
|
445
545
|
- the route is child-scoped
|
|
446
546
|
- the data is child-scoped
|
|
447
|
-
-
|
|
547
|
+
- the page is reached from a parent action, not from the main shell menu
|
|
548
|
+
- the page still needs a reliable parent identity even when the child list is empty
|
|
448
549
|
|
|
449
|
-
## Example 3: `comments`
|
|
550
|
+
## Example 3: `comments` under the contact host
|
|
450
551
|
|
|
451
552
|
This is the advanced example.
|
|
452
553
|
|
|
453
|
-
Sometimes a child resource
|
|
554
|
+
Sometimes a child resource should stay attached to the parent record, but still deserves its own routed child page.
|
|
454
555
|
|
|
455
556
|
Comments are a good example:
|
|
456
557
|
|
|
457
558
|
- they matter
|
|
458
559
|
- they need persistence
|
|
459
|
-
- they
|
|
460
|
-
- but
|
|
560
|
+
- they benefit from their own list, `new`, `view`, and `edit` routes
|
|
561
|
+
- but they still belong inside the contact experience rather than as a top-level destination
|
|
461
562
|
|
|
462
563
|
So the pattern is:
|
|
463
564
|
|
|
464
565
|
- keep the contact page as the real host
|
|
465
|
-
-
|
|
466
|
-
-
|
|
566
|
+
- keep the child route tree under that host
|
|
567
|
+
- let the generated placement surface the comments page as a contact child page
|
|
467
568
|
|
|
468
569
|
### Step 1: create the table
|
|
469
570
|
|
|
@@ -474,12 +575,13 @@ CREATE TABLE comments (
|
|
|
474
575
|
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
475
576
|
workspace_id BIGINT UNSIGNED NOT NULL,
|
|
476
577
|
contact_id BIGINT UNSIGNED NOT NULL,
|
|
477
|
-
author_user_id BIGINT UNSIGNED NULL,
|
|
478
578
|
body TEXT NOT NULL,
|
|
479
579
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
480
580
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
481
581
|
KEY idx_comments_workspace_id (workspace_id),
|
|
482
|
-
KEY idx_comments_contact_id (contact_id)
|
|
582
|
+
KEY idx_comments_contact_id (contact_id),
|
|
583
|
+
CONSTRAINT fk_comments_contact_id
|
|
584
|
+
FOREIGN KEY (contact_id) REFERENCES contacts(id)
|
|
483
585
|
);
|
|
484
586
|
```
|
|
485
587
|
|
|
@@ -493,7 +595,45 @@ npx jskit generate crud-server-generator scaffold \
|
|
|
493
595
|
--table-name comments
|
|
494
596
|
```
|
|
495
597
|
|
|
496
|
-
|
|
598
|
+
Then install the generated local package:
|
|
599
|
+
|
|
600
|
+
```bash
|
|
601
|
+
npm install
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
If you are developing against a local JSKIT checkout, relink the app before you run the UI:
|
|
605
|
+
|
|
606
|
+
```bash
|
|
607
|
+
npm run devlinks
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
### Step 3: refine the generated lookup metadata by hand
|
|
611
|
+
|
|
612
|
+
Open `packages/comments/src/shared/commentResource.js` and add `labelKey: "fullName"` to the generated `contactId` relation:
|
|
613
|
+
|
|
614
|
+
```js
|
|
615
|
+
contactId: {
|
|
616
|
+
type: "id",
|
|
617
|
+
required: true,
|
|
618
|
+
search: true,
|
|
619
|
+
relation: {
|
|
620
|
+
kind: "lookup",
|
|
621
|
+
namespace: "contacts",
|
|
622
|
+
valueKey: "id",
|
|
623
|
+
labelKey: "fullName"
|
|
624
|
+
},
|
|
625
|
+
belongsTo: "contacts",
|
|
626
|
+
as: "contact",
|
|
627
|
+
ui: { formControl: "autocomplete" },
|
|
628
|
+
...
|
|
629
|
+
}
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
This is the same manual refinement used in the `addresses` example: the scaffold can infer the lookup relationship from the foreign key, but it cannot infer which parent field should be used as the display label.
|
|
633
|
+
|
|
634
|
+
The next command also makes the parent-title mode explicit on purpose. That way changing the heading behavior later is just changing the value and rerunning with `--force`, not adding a new flag.
|
|
635
|
+
|
|
636
|
+
### Step 4: make the parent page able to host routed children
|
|
497
637
|
|
|
498
638
|
If the contact view page should stay visible while child comment routes render underneath it, first upgrade it into a routed host:
|
|
499
639
|
|
|
@@ -509,63 +649,71 @@ That is the point where the comments example deliberately overlaps with the `ui-
|
|
|
509
649
|
- CRUD scaffolding for the comments resource
|
|
510
650
|
- a routed host in the parent contact page
|
|
511
651
|
|
|
512
|
-
### Step 4:
|
|
652
|
+
### Step 4.5: make `comments` the default child page
|
|
513
653
|
|
|
514
|
-
|
|
654
|
+
This is a very common next step.
|
|
515
655
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
656
|
+
If the contact page should open directly on `Comments`, add an explicit redirect to the contact host page:
|
|
657
|
+
|
|
658
|
+
```vue
|
|
659
|
+
<script setup>
|
|
660
|
+
import { redirectToChild } from "@jskit-ai/kernel/client/pageRedirects";
|
|
661
|
+
|
|
662
|
+
definePage({
|
|
663
|
+
redirect: redirectToChild("comments")
|
|
664
|
+
});
|
|
665
|
+
</script>
|
|
522
666
|
```
|
|
523
667
|
|
|
524
|
-
|
|
668
|
+
In this example, that edit belongs in:
|
|
525
669
|
|
|
526
|
-
|
|
670
|
+
```text
|
|
671
|
+
src/pages/w/[workspaceSlug]/admin/contacts/[contactId]/index.vue
|
|
672
|
+
```
|
|
527
673
|
|
|
528
|
-
|
|
529
|
-
- the host contact page stays responsible for showing the comment list
|
|
530
|
-
- the generated child routes still handle things like `new`, `view`, and `edit`
|
|
674
|
+
This is the recommended pattern. Do **not** try to infer the default child page from placement order or "the first tab". Keep it explicit.
|
|
531
675
|
|
|
532
|
-
|
|
676
|
+
When this redirect is present:
|
|
533
677
|
|
|
534
|
-
|
|
678
|
+
- opening `/w/<workspaceSlug>/admin/contacts/<contactId>` lands on `/w/<workspaceSlug>/admin/contacts/<contactId>/comments`
|
|
679
|
+
- the parent contact page still stays visible
|
|
680
|
+
- the child page renders underneath it
|
|
535
681
|
|
|
536
|
-
|
|
537
|
-
- `useCrudAddEdit()`
|
|
538
|
-
- `useCrudView()` when needed
|
|
682
|
+
That last point matters. The contact page is still the host page. The redirect only changes which child route becomes the default landing destination.
|
|
539
683
|
|
|
540
|
-
|
|
684
|
+
### Step 5: generate the full child CRUD page
|
|
541
685
|
|
|
542
|
-
|
|
686
|
+
Now generate the comments UI under the contact host:
|
|
543
687
|
|
|
544
|
-
|
|
688
|
+
```bash
|
|
689
|
+
npx jskit generate crud-ui-generator crud \
|
|
690
|
+
w/[workspaceSlug]/admin/contacts/[contactId]/index/comments \
|
|
691
|
+
--resource-file packages/comments/src/shared/commentResource.js \
|
|
692
|
+
--id-param commentId \
|
|
693
|
+
--parent-title none \
|
|
694
|
+
--display-fields body
|
|
695
|
+
```
|
|
545
696
|
|
|
546
|
-
|
|
547
|
-
- archive / reopen buttons
|
|
548
|
-
- quick PATCH / POST / DELETE actions on one record
|
|
697
|
+
Do **not** pass `--operations view,new,edit` here. That would omit the list page and leave you with only child operation routes.
|
|
549
698
|
|
|
550
|
-
|
|
699
|
+
`--parent-title none` is the important difference from `addresses`.
|
|
551
700
|
|
|
552
|
-
|
|
553
|
-
- routed list/view loading
|
|
554
|
-
- `useCrudAddEdit()`
|
|
555
|
-
- real create/edit forms
|
|
556
|
-
- `useCommand()`
|
|
557
|
-
- live actions inside the page
|
|
701
|
+
The contact host page already shows the parent identity, so a generated heading like `Comments for Tony Mobily` is redundant. If you later decide you do want that heading, rerun the same command with `--parent-title contextual --force`.
|
|
558
702
|
|
|
559
|
-
|
|
703
|
+
This command gives you:
|
|
560
704
|
|
|
561
|
-
|
|
705
|
+
- `w/[workspaceSlug]/admin/contacts/[contactId]/index/comments/index.vue`
|
|
706
|
+
- `w/[workspaceSlug]/admin/contacts/[contactId]/index/comments/new.vue`
|
|
707
|
+
- `w/[workspaceSlug]/admin/contacts/[contactId]/index/comments/[commentId]/index.vue`
|
|
708
|
+
- `w/[workspaceSlug]/admin/contacts/[contactId]/index/comments/[commentId]/edit.vue`
|
|
562
709
|
|
|
563
|
-
|
|
710
|
+
Because this route root lives under `.../[contactId]/index/comments`, the generated placement is the desired one here: `Comments` appears as a child page under the contact host.
|
|
564
711
|
|
|
565
712
|
That is the real lesson of the `comments` example:
|
|
566
713
|
|
|
567
|
-
-
|
|
568
|
-
-
|
|
714
|
+
- the contact page stays the routed host
|
|
715
|
+
- the child CRUD gets its own list page
|
|
716
|
+
- the route still stays under the parent record experience
|
|
569
717
|
|
|
570
718
|
## When `scaffold-field` matters
|
|
571
719
|
|
|
@@ -597,8 +745,8 @@ It patches the canonical `schema` inside the shared resource file. That matters
|
|
|
597
745
|
The important thing is not memorizing commands. It is learning the three shapes:
|
|
598
746
|
|
|
599
747
|
- `contacts`: a normal top-level CRUD
|
|
600
|
-
- `addresses`: a child CRUD with its own routed list page
|
|
601
|
-
- `comments`: a child CRUD that lives
|
|
748
|
+
- `addresses`: a child CRUD with its own routed list page reached from the parent record view
|
|
749
|
+
- `comments`: a child CRUD that lives under the parent record host as a child page
|
|
602
750
|
|
|
603
751
|
Once you understand those three shapes, the two generator packages stop feeling like separate tools.
|
|
604
752
|
|
|
@@ -257,6 +257,37 @@ The route page is still a normal page file. What changes is the inferred placeme
|
|
|
257
257
|
|
|
258
258
|
So JSKIT is not switching to a different generator. It is switching the inferred placement behavior because the route tree now has a routed host above the new page.
|
|
259
259
|
|
|
260
|
+
### Making a child page the default landing route
|
|
261
|
+
|
|
262
|
+
This is important enough to state explicitly: if a routed host has child pages, and you want the bare parent URL to open one child immediately, use an explicit redirect.
|
|
263
|
+
|
|
264
|
+
Do **not** try to make the app "guess the first tab". Do **not** infer it from placement order. Keep the target explicit.
|
|
265
|
+
|
|
266
|
+
The standard pattern is:
|
|
267
|
+
|
|
268
|
+
```vue
|
|
269
|
+
<script setup>
|
|
270
|
+
import { redirectToChild } from "@jskit-ai/kernel/client/pageRedirects";
|
|
271
|
+
|
|
272
|
+
definePage({
|
|
273
|
+
redirect: redirectToChild("exports")
|
|
274
|
+
});
|
|
275
|
+
</script>
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
If this is placed on the host page, opening `/reports` lands on `/reports/exports`.
|
|
279
|
+
|
|
280
|
+
This is also the right pattern when the host page still renders shared content such as a title, tabs, summary panel, or `RouterView`. The parent route remains the host, and the child page simply becomes the default destination under it.
|
|
281
|
+
|
|
282
|
+
Why this is the recommended pattern:
|
|
283
|
+
|
|
284
|
+
- the destination is explicit and stable
|
|
285
|
+
- it survives later placement reordering
|
|
286
|
+
- it does not depend on which child link happens to render first
|
|
287
|
+
- it is easy to change later by editing one child segment
|
|
288
|
+
|
|
289
|
+
This is one of the most common things people want once they start using child-page hosts. Treat it as normal JSKIT routing, not a special hack.
|
|
290
|
+
|
|
260
291
|
## Nested pages under an `index.vue` host
|
|
261
292
|
|
|
262
293
|
Once `Reports` is a host, create a child page under it:
|
package/package.json
CHANGED
|
@@ -6,16 +6,18 @@ Use when:
|
|
|
6
6
|
- writing `definePage({ redirect: ... })`
|
|
7
7
|
- seeding settings landing pages such as `/home/settings`
|
|
8
8
|
- wiring a section shell that should land on a real child page first
|
|
9
|
+
- making a routed host page open a default child tab such as `comments`
|
|
9
10
|
|
|
10
11
|
Check first:
|
|
11
12
|
|
|
12
|
-
- whether the page is only a landing route for child pages
|
|
13
|
+
- whether the page is only a landing route for child pages or a host page that should always land on one child first
|
|
13
14
|
- whether the destination child route is explicit and stable
|
|
14
15
|
- whether the redirect should preserve incoming query and hash
|
|
15
16
|
|
|
16
17
|
Rules:
|
|
17
18
|
|
|
18
19
|
- For “index page lands on child page” redirects, use `redirectToChild()` from `@jskit-ai/kernel/client/pageRedirects`.
|
|
20
|
+
- The same helper is also the explicit pattern for “this host page should open child X by default”.
|
|
19
21
|
- Do not hand-build child redirects with string surgery such as `` `${String(to.path || "").replace(/\\/$/, "")}/general` ``.
|
|
20
22
|
- Keep the destination explicit. Do not infer it from placements, menu order, or “first child page wins” behavior.
|
|
21
23
|
- If the redirect is just “go to this child page”, prefer:
|
|
@@ -28,6 +30,8 @@ definePage({
|
|
|
28
30
|
});
|
|
29
31
|
```
|
|
30
32
|
|
|
33
|
+
- This can live on a host page that still renders its own shell and `RouterView`. The parent route remains matched and the child route renders underneath it.
|
|
34
|
+
|
|
31
35
|
Why:
|
|
32
36
|
|
|
33
37
|
- the helper centralizes slash handling
|
|
@@ -22,6 +22,7 @@ Startup navigation stays in `KERNEL_MAP.md`.
|
|
|
22
22
|
- [database-runtime](/packages/agent-docs/reference/autogen/packages/database-runtime.md)
|
|
23
23
|
- [database-runtime-mysql](/packages/agent-docs/reference/autogen/packages/database-runtime-mysql.md)
|
|
24
24
|
- [database-runtime-postgres](/packages/agent-docs/reference/autogen/packages/database-runtime-postgres.md)
|
|
25
|
+
- [feature-server-generator](/packages/agent-docs/reference/autogen/packages/feature-server-generator.md)
|
|
25
26
|
- [http-runtime](/packages/agent-docs/reference/autogen/packages/http-runtime.md)
|
|
26
27
|
- [json-rest-api-core](/packages/agent-docs/reference/autogen/packages/json-rest-api-core.md)
|
|
27
28
|
- [kernel](/packages/agent-docs/reference/autogen/packages/kernel.md)
|
|
@@ -216,6 +216,7 @@ Exports
|
|
|
216
216
|
- `MAX_INPUT_CHARS`
|
|
217
217
|
- `MAX_HISTORY_MESSAGES`
|
|
218
218
|
- `assistantResource`
|
|
219
|
+
- `assistantConversationOutputValidator`
|
|
219
220
|
|
|
220
221
|
### `src/shared/assistantSettingsResource.js`
|
|
221
222
|
Exports
|
|
@@ -245,14 +246,26 @@ Exports
|
|
|
245
246
|
- `MAX_INPUT_CHARS`
|
|
246
247
|
- `MAX_HISTORY_MESSAGES`
|
|
247
248
|
- `assistantResource`
|
|
249
|
+
- `assistantConversationOutputValidator`
|
|
248
250
|
- `MAX_SYSTEM_PROMPT_CHARS`
|
|
249
251
|
- `assistantConfigResource`
|
|
252
|
+
- `ASSISTANT_SETTINGS_TRANSPORT`
|
|
253
|
+
- `ASSISTANT_SETTINGS_UPDATE_TRANSPORT`
|
|
254
|
+
- `ASSISTANT_CONVERSATIONS_TRANSPORT`
|
|
255
|
+
- `ASSISTANT_CONVERSATION_MESSAGES_TRANSPORT`
|
|
250
256
|
- `assistantSettingsEvents`
|
|
251
257
|
- `ASSISTANT_CONVERSATION_STATUSES`
|
|
252
258
|
- `normalizeConversationStatus`
|
|
253
259
|
- `parseJsonObject`
|
|
254
260
|
- `toPositiveInteger`
|
|
255
261
|
|
|
262
|
+
### `src/shared/jsonApiTransports.js`
|
|
263
|
+
Exports
|
|
264
|
+
- `ASSISTANT_SETTINGS_TRANSPORT`
|
|
265
|
+
- `ASSISTANT_SETTINGS_UPDATE_TRANSPORT`
|
|
266
|
+
- `ASSISTANT_CONVERSATIONS_TRANSPORT`
|
|
267
|
+
- `ASSISTANT_CONVERSATION_MESSAGES_TRANSPORT`
|
|
268
|
+
|
|
256
269
|
### `src/shared/queryKeys.js`
|
|
257
270
|
Exports
|
|
258
271
|
- `ASSISTANT_QUERY_KEY_PREFIX`
|
|
@@ -100,6 +100,8 @@ Local functions
|
|
|
100
100
|
- `sendPreStreamErrorResponse(reply, error)`
|
|
101
101
|
- `resolveRouteRequestState(request, { resolveCurrentAppConfig = () => ({}), kind = "runtime", requiresWorkspace = false, workspaceScopeSupport = null } = {})`
|
|
102
102
|
- `buildChatStreamActionInput(routeInput = {}, requestBody = {})`
|
|
103
|
+
- `resolveAssistantSettingsRecordId(record = {})`
|
|
104
|
+
- `resolveAssistantConversationMessagesRecordId(record = {})`
|
|
103
105
|
- `registerSettingsRoutes(router, resolveCurrentAppConfig, { requiresWorkspace = false, workspaceScopeSupport = null } = {})`
|
|
104
106
|
- `registerRuntimeRoutes(router, resolveCurrentAppConfig, { requiresWorkspace = false, workspaceScopeSupport = null } = {})`
|
|
105
107
|
|
|
@@ -304,6 +304,10 @@ Local functions
|
|
|
304
304
|
- `createRecordRelationshipsResolver(definition = null)`
|
|
305
305
|
- `createRequestRelationshipMapper(definition = null)`
|
|
306
306
|
- `resolveOutputAttributeExcludeKeys(resource = {})`
|
|
307
|
+
- `resolveLookupContainerKey(resource = {})`
|
|
308
|
+
- `normalizeLookupId(value)`
|
|
309
|
+
- `normalizeIncludedLookupRecord(source = null, fallbackId = null)`
|
|
310
|
+
- `createLookupIncludedResolver(definition = null, { lookupContainerKey = "" } = {})`
|
|
307
311
|
|
|
308
312
|
### `src/server/serviceEvents.js`
|
|
309
313
|
Exports
|
|
@@ -30,6 +30,7 @@ Local functions
|
|
|
30
30
|
- `resolveListTargetFile(targetRoot = "")`
|
|
31
31
|
- `parseOperationsOption(options)`
|
|
32
32
|
- `parseDisplayFieldsOption(options)`
|
|
33
|
+
- `parseParentTitleOption(options)`
|
|
33
34
|
- `validateDisplayFieldsForOperation(selectedFieldKeys, fields, operationName)`
|
|
34
35
|
- `filterDisplayFields(selectedFieldKeys, fields)`
|
|
35
36
|
- `rewriteGeneratedBlockIndent(source = "", { trimPrefix = "", addPrefix = "" } = {})`
|
|
@@ -46,6 +47,8 @@ Local functions
|
|
|
46
47
|
- `resolveTargetRootRelativeRoutePath(pageTarget = {})`
|
|
47
48
|
- `resolveMenuToPropLine(linkTo = "")`
|
|
48
49
|
- `resolveCrudRelativePath(namespace = "")`
|
|
50
|
+
- `buildListParentTitleImportLine(parentTitleMode = "contextual")`
|
|
51
|
+
- `buildListHeadingTitleSetup({ parentTitleMode = "contextual", resourceNamespace = "", routeTitle = "Records" } = {})`
|
|
49
52
|
|
|
50
53
|
### `src/server/resourceSupport.js`
|
|
51
54
|
Exports
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# packages/feature-server-generator
|
|
2
|
+
|
|
3
|
+
Generated by `npm run agent-docs:build`.
|
|
4
|
+
Do not edit manually.
|
|
5
|
+
|
|
6
|
+
Generated inventory for `packages/feature-server-generator`.
|
|
7
|
+
Use this on demand; do not load the full index at startup.
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
- Source: `packages/feature-server-generator/**/*{.js,.mjs,.cjs,.vue}`
|
|
11
|
+
- Excludes: `test/`, `tests/`, `__tests__/`, `*.test.*`, `*.spec.*`, `*.vitest.*`, `node_modules/`, `dist/`, `coverage/`, `docs/`, `LEGACY/`, `.vitepress/cache/`, `.vitepress/dist/`
|
|
12
|
+
|
|
13
|
+
## Sections
|
|
14
|
+
|
|
15
|
+
### src
|
|
16
|
+
|
|
17
|
+
### `src/server/buildTemplateContext.js`
|
|
18
|
+
Exports
|
|
19
|
+
- `buildTemplateContext({ options = {} } = {})`
|
|
20
|
+
Local functions
|
|
21
|
+
- `splitTextIntoWords(value)`
|
|
22
|
+
- `wordsToPascal(words = [])`
|
|
23
|
+
- `wordsToKebab(words = [])`
|
|
24
|
+
- `wordsToCamel(words = [])`
|
|
25
|
+
- `normalizeFeatureName(value)`
|
|
26
|
+
- `normalizeSurfaceId(value)`
|
|
27
|
+
- `normalizeRoutePrefix(value)`
|
|
28
|
+
- `quoteArray(values = [])`
|
|
29
|
+
- `buildProviderContext({ featureName, mode, routePrefix, surface })`
|
|
30
|
+
- `buildActionsContext({ surface })`
|
|
31
|
+
- `buildServiceContext({ featureName, mode })`
|
|
32
|
+
- `buildRouteContext({ surface })`
|
|
33
|
+
- `buildDescriptorContext({ featureName, mode })`
|
|
34
|
+
|
|
35
|
+
### templates
|
|
36
|
+
|
|
37
|
+
### `templates/src/local-package/package.descriptor.mjs`
|
|
38
|
+
Exports
|
|
39
|
+
- None
|
|
40
|
+
|
|
41
|
+
### `templates/src/local-package/server/actions.js`
|
|
42
|
+
Exports
|
|
43
|
+
- `featureActions`
|
|
44
|
+
|
|
45
|
+
### `templates/src/local-package/server/FeatureProvider.js`
|
|
46
|
+
Exports
|
|
47
|
+
- `${option:feature-name|pascal`
|
|
48
|
+
|
|
49
|
+
### `templates/src/local-package/server/inputSchemas.js`
|
|
50
|
+
Exports
|
|
51
|
+
- `statusQueryInputValidator`
|
|
52
|
+
- `executeCommandInputValidator`
|
|
53
|
+
|
|
54
|
+
### `templates/src/local-package/server/registerRoutes.js`
|
|
55
|
+
Exports
|
|
56
|
+
- `registerRoutes(app, { routeSurface = "", routeRelativePath = "" } = {})`
|
|
57
|
+
|
|
58
|
+
### `templates/src/local-package/server/repositoryCustomKnex.js`
|
|
59
|
+
Exports
|
|
60
|
+
- `createRepository({ knex } = {})`
|
|
61
|
+
|
|
62
|
+
### `templates/src/local-package/server/repositoryJsonRest.js`
|
|
63
|
+
Exports
|
|
64
|
+
- `createRepository({ api } = {})`
|
|
65
|
+
|
|
66
|
+
### `templates/src/local-package/server/service.js`
|
|
67
|
+
Exports
|
|
68
|
+
- `createService({ featureRepository } = {})`
|
|
69
|
+
|
|
70
|
+
### root
|
|
71
|
+
|
|
72
|
+
### `package.descriptor.mjs`
|
|
73
|
+
Exports
|
|
74
|
+
- None
|
|
@@ -28,11 +28,11 @@ Exports
|
|
|
28
28
|
- `createJsonApiRelationship(resourceType = "", id = null)`
|
|
29
29
|
- `createJsonRestResourceScopeOptions(resource = {}, { writeSerializers = {}, normalizeId = null } = {})`
|
|
30
30
|
- `createJsonRestContext(context = null)`
|
|
31
|
+
- `extractJsonRestCollectionRows(payload = null)`
|
|
31
32
|
- `isJsonRestResourceMissingError(error = null)`
|
|
32
33
|
- `returnNullWhenJsonRestResourceMissing(run)`
|
|
33
34
|
- `resolveWorkspaceScopeValue(context = null)`
|
|
34
35
|
- `resolveUserScopeValue(context = null)`
|
|
35
|
-
- `simplifyJsonApiDocument(payload = {})`
|
|
36
36
|
- `createJsonRestApiHost({ knex })`
|
|
37
37
|
- `registerJsonRestApiHost(app)`
|
|
38
38
|
Local functions
|
|
@@ -43,10 +43,6 @@ Local functions
|
|
|
43
43
|
- `normalizeJsonRestObject(value)`
|
|
44
44
|
- `normalizeJsonRestList(value)`
|
|
45
45
|
- `extractJsonApiInputRelationships(attributes = {}, resource = null, relationships = null)`
|
|
46
|
-
- `normalizeJsonApiResourceObject(resource = {})`
|
|
47
|
-
- `buildJsonApiIncludedIndex(payload = {})`
|
|
48
|
-
- `simplifyJsonApiRelationshipData(data, { includedIndex = null, seen = null } = {})`
|
|
49
|
-
- `simplifyJsonApiResourceObject(resource = {}, { includedIndex = null, seen = null } = {})`
|
|
50
46
|
|
|
51
47
|
### root
|
|
52
48
|
|
|
@@ -84,6 +84,7 @@ Exports
|
|
|
84
84
|
### `src/client/composables/crud/crudJsonApiTransportSupport.js`
|
|
85
85
|
Exports
|
|
86
86
|
- `inferCrudJsonApiTransport(resource = null, { mode = "", operationName = "" } = {})`
|
|
87
|
+
- `inferCrudLookupJsonApiTransport({ namespace = "", apiPath = "" } = {})`
|
|
87
88
|
- `resolveCrudJsonApiTransport(transport = null, resource = null, options = {})`
|
|
88
89
|
- `resolveLookupFieldMap(resource = null)`
|
|
89
90
|
Local functions
|
|
@@ -95,29 +95,17 @@ Exports
|
|
|
95
95
|
- `MainClientProvider`
|
|
96
96
|
- `registerMainClientComponent(token, resolveComponent)`
|
|
97
97
|
|
|
98
|
-
### `templates/base-shell/packages/main/src/server/controllers/index.js`
|
|
99
|
-
Exports
|
|
100
|
-
- None
|
|
101
|
-
|
|
102
98
|
### `templates/base-shell/packages/main/src/server/index.js`
|
|
103
99
|
Exports
|
|
104
100
|
- `MainServiceProvider`
|
|
105
101
|
|
|
106
|
-
### `templates/base-shell/packages/main/src/server/
|
|
102
|
+
### `templates/base-shell/packages/main/src/server/loadAppConfig.js`
|
|
107
103
|
Exports
|
|
108
|
-
- `
|
|
109
|
-
|
|
110
|
-
### `templates/base-shell/packages/main/src/server/routes/index.js`
|
|
111
|
-
Exports
|
|
112
|
-
- None
|
|
113
|
-
|
|
114
|
-
### `templates/base-shell/packages/main/src/server/services/index.js`
|
|
115
|
-
Exports
|
|
116
|
-
- None
|
|
104
|
+
- `loadAppConfig({ moduleUrl = import.meta.url } = {})`
|
|
117
105
|
|
|
118
|
-
### `templates/base-shell/packages/main/src/server/
|
|
106
|
+
### `templates/base-shell/packages/main/src/server/MainServiceProvider.js`
|
|
119
107
|
Exports
|
|
120
|
-
- `
|
|
108
|
+
- `MainServiceProvider`
|
|
121
109
|
|
|
122
110
|
### `templates/base-shell/packages/main/src/shared/index.js`
|
|
123
111
|
Exports
|
|
@@ -371,6 +371,7 @@ Exports
|
|
|
371
371
|
Local functions
|
|
372
372
|
- `collectDeclaredPackageNames(packageJson = {})`
|
|
373
373
|
- `verifySymlinkTarget(targetPath = "", sourceDir = "", { packageName = "" } = {})`
|
|
374
|
+
- `replaceWithSymlink(targetPath = "", sourceDir = "", { packageName = "" } = {})`
|
|
374
375
|
- `maybeLinkCompanionPackages({ appRoot = "", repoRoot = "", stdout, createCliError })`
|
|
375
376
|
|
|
376
377
|
### `src/server/commandHandlers/appCommands/release.js`
|
|
@@ -429,6 +430,10 @@ Exports
|
|
|
429
430
|
Local functions
|
|
430
431
|
- `collectTokenMatches(source = "", pattern = COMPONENT_TOKEN_PATTERN)`
|
|
431
432
|
- `appendTokenSource(map, token = "", source = "")`
|
|
433
|
+
- `toShortPackageId(packageId = "")`
|
|
434
|
+
- `resolveGeneratorPrimarySubcommand(packageEntry = {})`
|
|
435
|
+
- `resolveGeneratorDescription(packageEntry = {})`
|
|
436
|
+
- `resolveGeneratorQuickStartRows(packageEntry = {}, { limit = 3 } = {})`
|
|
432
437
|
- `isLinkItemToken(token = "")`
|
|
433
438
|
- `readFileIfExists(filePath = "")`
|
|
434
439
|
- `resolveDescriptorFromLockEntry({ appRoot = "", packageId = "", installedPackageEntry = {} } = {})`
|
|
@@ -469,6 +474,7 @@ Local functions
|
|
|
469
474
|
- `normalizeHelpParagraphRows(rawValue = "")`
|
|
470
475
|
- `appendHelpLongDescription(lines = [], rawLongDescription = "", { color = null } = {})`
|
|
471
476
|
- `appendHelpExamples(lines = [], exampleRows = [], { color = null } = {})`
|
|
477
|
+
- `appendHelpExamplesWithHeading(lines = [], exampleRows = [], heading = "", { color = null } = {})`
|
|
472
478
|
- `appendHelpNotes(lines = [], rawNotes = [], { color = null } = {})`
|
|
473
479
|
- `appendSeparatedBlocks(lines = [], blocks = [])`
|
|
474
480
|
- `resolveGeneratorSubcommandMetadata(packageEntry = {})`
|
|
@@ -477,6 +483,7 @@ Local functions
|
|
|
477
483
|
- `formatPositionalArgSummary(arg = {}, { color = null } = {})`
|
|
478
484
|
- `findGeneratorSubcommandRow(packageEntry = {}, subcommandName = "")`
|
|
479
485
|
- `buildSubcommandOptionRows(optionRows = [], subcommandRow = {})`
|
|
486
|
+
- `resolvePrimaryGeneratorQuickStartRows(packageEntry = {}, { limit = 3 } = {})`
|
|
480
487
|
|
|
481
488
|
### `src/server/commandHandlers/packageCommands/generate.js`
|
|
482
489
|
Exports
|
|
@@ -563,6 +570,9 @@ Local functions
|
|
|
563
570
|
### `src/server/commandHandlers/show/renderPackageText.js`
|
|
564
571
|
Exports
|
|
565
572
|
- `renderPackagePayloadText({ payload, provides, requires, capabilityDetails, options, stdout, color, resolveWrapWidth, writeWrappedItems, normalizeRelativePosixPath, formatPackageSubpathImport, normalizePlacementOutlets, normalizePlacementContributions, shouldShowPackageExportTarget, classifyExportedSymbols, deriveProviderDisplayName } = {})`
|
|
573
|
+
Local functions
|
|
574
|
+
- `resolveGeneratorSubcommandRows(payload = {})`
|
|
575
|
+
- `resolveOwnershipGuidance(payload = {})`
|
|
566
576
|
|
|
567
577
|
### `src/server/core/argParser.js`
|
|
568
578
|
Exports
|