@jskit-ai/agent-docs 0.1.131 → 0.1.133
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-extras/assistant.md +29 -605
- package/guide/agent/app-extras/mobile-capacitor.md +29 -362
- package/guide/agent/app-extras/realtime.md +29 -277
- package/guide/agent/app-setup/a-more-interesting-shell.md +44 -815
- package/guide/agent/app-setup/authentication.md +43 -1073
- package/guide/agent/app-setup/console.md +26 -298
- package/guide/agent/app-setup/database-layer.md +110 -790
- package/guide/agent/app-setup/existing-application-migration.md +273 -0
- package/guide/agent/app-setup/initial-scaffolding.md +50 -784
- package/guide/agent/app-setup/multi-homing.md +39 -712
- package/guide/agent/app-setup/quickstart.md +43 -179
- package/guide/agent/app-setup/users.md +34 -353
- package/guide/agent/index.md +17 -23
- package/package.json +2 -2
- package/patterns/INDEX.md +7 -7
- package/patterns/child-cruds.md +3 -3
- package/patterns/client-requests.md +6 -6
- package/patterns/crud-authoring.md +94 -0
- package/patterns/crud-links.md +1 -1
- package/patterns/feature-package/PATTERN.md +108 -0
- package/patterns/feature-package/example/booking-engine/package.json +48 -0
- package/patterns/feature-package/example/booking-engine/src/server/BookingEngineProvider.js +33 -0
- package/patterns/feature-package/example/booking-engine/src/server/actions.js +26 -0
- package/patterns/feature-package/example/booking-engine/src/server/inputSchemas.js +19 -0
- package/patterns/feature-package/example/variations/AvailabilityEngineProvider.js +31 -0
- package/patterns/feature-package/example/variations/InvoiceRollupProvider.js +36 -0
- package/patterns/feature-package/example/variations/customKnexRepository.js +34 -0
- package/patterns/feature-package/example/variations/orchestratorService.js +23 -0
- package/patterns/filters.md +8 -8
- package/patterns/live-actions.md +5 -18
- package/patterns/minimal-foundation/PATTERN.md +98 -0
- package/patterns/minimal-foundation/example/.nvmrc +1 -0
- package/patterns/minimal-foundation/example/AGENTS.md +17 -0
- package/patterns/minimal-foundation/example/Procfile +2 -0
- package/patterns/minimal-foundation/example/app.json +14 -0
- package/patterns/minimal-foundation/example/bin/develop.js +71 -0
- package/patterns/minimal-foundation/example/bin/server.js +8 -0
- package/patterns/minimal-foundation/example/config/public.js +40 -0
- package/patterns/minimal-foundation/example/config/server.js +1 -0
- package/patterns/minimal-foundation/example/config/surfaceAccessPolicies.js +3 -0
- package/patterns/minimal-foundation/example/eslint.config.mjs +19 -0
- package/patterns/minimal-foundation/example/favicon.svg +7 -0
- package/patterns/minimal-foundation/example/gitignore +9 -0
- package/patterns/minimal-foundation/example/index.html +13 -0
- package/patterns/minimal-foundation/example/jsconfig.json +8 -0
- package/patterns/minimal-foundation/example/package.json +57 -0
- package/patterns/minimal-foundation/example/packages/main/package.json +42 -0
- package/patterns/minimal-foundation/example/packages/main/src/shared/index.js +10 -0
- package/patterns/minimal-foundation/example/packages/main/src/shared/schemas/index.js +22 -0
- package/patterns/minimal-foundation/example/playwright.config.mjs +31 -0
- package/patterns/minimal-foundation/example/server/lib/runtimeEnv.js +45 -0
- package/patterns/minimal-foundation/example/server/lib/surfaceRuntime.js +10 -0
- package/patterns/minimal-foundation/example/server.js +195 -0
- package/patterns/minimal-foundation/example/src/App.vue +13 -0
- package/patterns/minimal-foundation/example/src/main.js +85 -0
- package/patterns/minimal-foundation/example/src/pages/home/index.vue +48 -0
- package/patterns/minimal-foundation/example/src/pages/home.vue +13 -0
- package/patterns/minimal-foundation/example/src/views/NotFound.vue +13 -0
- package/patterns/minimal-foundation/example/tests/client/smoke.vitest.js +7 -0
- package/patterns/minimal-foundation/example/tests/e2e/base-shell.spec.ts +23 -0
- package/patterns/minimal-foundation/example/tests/server/smoke.test.js +16 -0
- package/patterns/minimal-foundation/example/vite.config.mjs +81 -0
- package/patterns/page-scaffolding.md +20 -17
- package/patterns/placements.md +17 -15
- package/patterns/row-policies.md +4 -5
- package/patterns/server-search.md +3 -3
- package/patterns/shell-foundation/PATTERN.md +104 -0
- package/patterns/shell-foundation/example/.nvmrc +1 -0
- package/patterns/shell-foundation/example/AGENTS.md +17 -0
- package/patterns/shell-foundation/example/Procfile +2 -0
- package/patterns/shell-foundation/example/app.json +14 -0
- package/patterns/shell-foundation/example/bin/develop.js +71 -0
- package/patterns/shell-foundation/example/bin/server.js +8 -0
- package/patterns/shell-foundation/example/config/public.js +40 -0
- package/patterns/shell-foundation/example/config/server.js +1 -0
- package/patterns/shell-foundation/example/config/surfaceAccessPolicies.js +3 -0
- package/patterns/shell-foundation/example/eslint.config.mjs +19 -0
- package/patterns/shell-foundation/example/favicon.svg +7 -0
- package/patterns/shell-foundation/example/gitignore +9 -0
- package/patterns/shell-foundation/example/index.html +13 -0
- package/patterns/shell-foundation/example/jsconfig.json +8 -0
- package/patterns/shell-foundation/example/package.json +59 -0
- package/patterns/shell-foundation/example/packages/main/package.json +56 -0
- package/patterns/shell-foundation/example/packages/main/src/client/index.js +9 -0
- package/patterns/shell-foundation/example/packages/main/src/client/providers/MainClientProvider.js +18 -0
- package/patterns/shell-foundation/example/packages/main/src/shared/index.js +10 -0
- package/patterns/shell-foundation/example/packages/main/src/shared/schemas/index.js +22 -0
- package/patterns/shell-foundation/example/playwright.config.mjs +31 -0
- package/patterns/shell-foundation/example/server/lib/runtimeEnv.js +45 -0
- package/patterns/shell-foundation/example/server/lib/surfaceRuntime.js +10 -0
- package/patterns/shell-foundation/example/server.js +195 -0
- package/patterns/shell-foundation/example/src/App.vue +11 -0
- package/patterns/shell-foundation/example/src/components/ShellLayout.vue +12 -0
- package/patterns/shell-foundation/example/src/components/menus/MenuLinkItem.vue +30 -0
- package/patterns/shell-foundation/example/src/components/menus/SurfaceAwareMenuLinkItem.vue +42 -0
- package/patterns/shell-foundation/example/src/components/menus/TabLinkItem.vue +42 -0
- package/patterns/shell-foundation/example/src/error.js +19 -0
- package/patterns/shell-foundation/example/src/main.js +85 -0
- package/patterns/shell-foundation/example/src/pages/home/index.vue +116 -0
- package/patterns/shell-foundation/example/src/pages/home/settings/general/index.vue +40 -0
- package/patterns/shell-foundation/example/src/pages/home/settings/index.vue +7 -0
- package/patterns/shell-foundation/example/src/pages/home/settings.vue +109 -0
- package/patterns/shell-foundation/example/src/pages/home.vue +20 -0
- package/patterns/shell-foundation/example/src/placement.js +56 -0
- package/patterns/shell-foundation/example/src/placementTopology.js +149 -0
- package/patterns/shell-foundation/example/src/views/NotFound.vue +13 -0
- package/patterns/shell-foundation/example/tests/client/smoke.vitest.js +7 -0
- package/patterns/shell-foundation/example/tests/e2e/adaptive-shell.spec.ts +10 -0
- package/patterns/shell-foundation/example/tests/e2e/base-shell.spec.ts +23 -0
- package/patterns/shell-foundation/example/tests/server/smoke.test.js +16 -0
- package/patterns/shell-foundation/example/vite.config.mjs +81 -0
- package/patterns/ui-contract.md +56 -0
- package/patterns/ui-testing.md +10 -12
- package/reference/autogen/KERNEL_MAP.md +29 -107
- package/reference/autogen/PATTERN_INDEX.md +230 -0
- package/reference/autogen/README.md +4 -8
- package/reference/autogen/packages/agent-docs.md +259 -0
- package/reference/autogen/packages/assistant-core.md +3 -3
- package/reference/autogen/packages/assistant-runtime.md +32 -17
- package/reference/autogen/packages/auth-core.md +31 -33
- package/reference/autogen/packages/auth-provider-local-core.md +4 -12
- package/reference/autogen/packages/auth-provider-local-db-core.md +4 -4
- package/reference/autogen/packages/auth-provider-supabase-core.md +14 -18
- package/reference/autogen/packages/auth-web.md +42 -22
- package/reference/autogen/packages/console-core.md +8 -25
- package/reference/autogen/packages/console-web.md +5 -5
- package/reference/autogen/packages/crud-core.md +61 -17
- package/reference/autogen/packages/database-runtime-mysql.md +12 -2
- package/reference/autogen/packages/database-runtime-postgres.md +12 -2
- package/reference/autogen/packages/database-runtime.md +26 -25
- package/reference/autogen/packages/google-rewarded-core.md +19 -104
- package/reference/autogen/packages/http-runtime.md +4 -8
- package/reference/autogen/packages/http-web.md +32 -0
- package/reference/autogen/packages/json-rest-api-core.md +4 -6
- package/reference/autogen/packages/kernel.md +109 -390
- package/reference/autogen/packages/mobile-capacitor.md +2 -13
- package/reference/autogen/packages/realtime.md +29 -26
- package/reference/autogen/packages/resource-crud-core.md +6 -0
- package/reference/autogen/packages/shell-web.md +69 -54
- package/reference/autogen/packages/storage-runtime.md +3 -3
- package/reference/autogen/packages/uploads-image-web.md +0 -1
- package/reference/autogen/packages/uploads-runtime.md +3 -3
- package/reference/autogen/packages/users-core.md +45 -90
- package/reference/autogen/packages/users-web.md +5 -7
- package/reference/autogen/packages/workspaces-core.md +53 -74
- package/reference/autogen/packages/workspaces-web.md +15 -16
- package/reference/autogen/tooling/jskit-catalog.md +34 -0
- package/reference/autogen/tooling/testUtils.md +4 -4
- package/skills/jskit/SKILL.md +38 -31
- package/skills/jskit/references/app-operations.md +68 -53
- package/skills/jskit/references/crud-operations.md +58 -106
- package/skills/jskit/references/material-3.md +1 -1
- package/skills/jskit/references/ui-operations.md +41 -44
- package/templates/app/AGENTS.md +7 -3
- package/guide/agent/app-setup/upgrade-beta-1-to-final.md +0 -252
- package/guide/agent/app-setup/working-with-the-jskit-cli.md +0 -325
- package/guide/agent/generators/advanced-cruds.md +0 -1935
- package/guide/agent/generators/crud-generators.md +0 -948
- package/guide/agent/generators/intro.md +0 -65
- package/guide/agent/generators/row-policies.md +0 -537
- package/guide/agent/generators/ui-generators.md +0 -690
- package/patterns/crud-scaffolding.md +0 -198
- package/patterns/generated-ui-contract-tracking.md +0 -66
- package/reference/autogen/packages/assistant.md +0 -68
- package/reference/autogen/packages/crud-server-generator.md +0 -215
- package/reference/autogen/packages/crud-ui-generator.md +0 -192
- package/reference/autogen/packages/feature-server-generator.md +0 -65
- package/reference/autogen/packages/ui-generator.md +0 -127
- package/reference/autogen/tooling/create-app.md +0 -317
- package/reference/autogen/tooling/jskit-cli.md +0 -933
- package/reference/autogen/tooling/test-support.md +0 -27
package/guide/agent/index.md
CHANGED
|
@@ -2,41 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
# Guide
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
JSKIT is an AI-first framework and tested source-pattern library. It owns
|
|
6
|
+
strong runtime APIs, package composition contracts, and examples an agent can
|
|
7
|
+
apply or adapt. It does not prescribe an agent host or project orchestrator.
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## Table of Contents
|
|
10
|
-
|
|
11
|
-
### App Setup
|
|
9
|
+
## Start here
|
|
12
10
|
|
|
13
11
|
- [Quickstart](/guide/app-setup/quickstart)
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
- [A
|
|
12
|
+
- [Application foundations](/guide/app-setup/initial-scaffolding)
|
|
13
|
+
- [Migrate an existing application](/guide/app-setup/existing-application-migration)
|
|
14
|
+
- [A more interesting shell](/guide/app-setup/a-more-interesting-shell)
|
|
17
15
|
- [Authentication](/guide/app-setup/authentication)
|
|
18
|
-
- [Database
|
|
16
|
+
- [Database layer](/guide/app-setup/database-layer)
|
|
19
17
|
- [Users](/guide/app-setup/users)
|
|
20
18
|
- [Multi-homing](/guide/app-setup/multi-homing)
|
|
21
19
|
- [Console](/guide/app-setup/console)
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
## Optional capabilities
|
|
24
22
|
|
|
25
23
|
- [Mobile Capacitor](/guide/app-extras/mobile-capacitor)
|
|
26
24
|
- [Realtime](/guide/app-extras/realtime)
|
|
27
25
|
- [Assistant](/guide/app-extras/assistant)
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- [Intro](/guide/generators/intro)
|
|
32
|
-
- [UI Generators](/guide/generators/ui-generators)
|
|
33
|
-
- [CRUD Generators](/guide/generators/crud-generators)
|
|
34
|
-
- [Advanced CRUDs](/guide/generators/advanced-cruds)
|
|
27
|
+
## How to use the guide
|
|
35
28
|
|
|
36
|
-
|
|
29
|
+
Begin with the Quickstart and one application-foundation pattern. Add only the
|
|
30
|
+
capabilities selected by the product. For implementation, the installed JSKIT
|
|
31
|
+
skill routes the agent to package-owned `PATTERN.md` files and the smallest
|
|
32
|
+
relevant public API reference.
|
|
37
33
|
|
|
38
|
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
- Jump into `Generators` if you already understand the runtime packages and want app-owned scaffolding workflows.
|
|
42
|
-
- Inside `Generators`, read `CRUD Generators` before `Advanced CRUDs`: the first chapter teaches the workflow, and the second explains the generated anatomy and customization points.
|
|
34
|
+
There are no generator or CLI chapters. Their useful source examples are
|
|
35
|
+
package-owned patterns; their questionnaire, mutation, and provenance machinery
|
|
36
|
+
is not part of JSKIT.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/agent-docs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.133",
|
|
4
4
|
"description": "Distributed JSKIT agent references, prompts, guides, and generated reference maps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"templates"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
|
-
"test": "node --test"
|
|
14
|
+
"test": "node --test test/*.test.js"
|
|
15
15
|
},
|
|
16
16
|
"engines": {
|
|
17
17
|
"node": "^22.13.0 || ^24.0.0 || ^26.0.0"
|
package/patterns/INDEX.md
CHANGED
|
@@ -14,12 +14,12 @@ How to use it:
|
|
|
14
14
|
- `placements.md`
|
|
15
15
|
- surfaces, app/admin/home/console, "which surface", route ownership, placement visibility
|
|
16
16
|
- `surfaces.md`
|
|
17
|
-
- page, route page, placeholder page, screen stub, menu-linked page,
|
|
17
|
+
- page, route page, placeholder page, screen stub, menu-linked page, placements
|
|
18
18
|
- `page-scaffolding.md`
|
|
19
19
|
- child crud, nested crud, embedded list, subroute, separate page, parent/child layout
|
|
20
20
|
- `child-cruds.md`
|
|
21
|
-
- crud
|
|
22
|
-
- `crud-
|
|
21
|
+
- crud, crud server, crud ui, resource contract, table creation, migrations, ownership, JSON API, standard screens
|
|
22
|
+
- `crud-authoring.md`
|
|
23
23
|
- CRUD links, record placeholders, `paths.page()`, `resolveViewUrl`, `resolveEditUrl`, `resolveParams`
|
|
24
24
|
- `crud-links.md`
|
|
25
25
|
- `definePage`, redirect, child redirect, settings landing, `redirectToChild`
|
|
@@ -30,8 +30,8 @@ How to use it:
|
|
|
30
30
|
- `client-requests.md`
|
|
31
31
|
- playwright, browser test, e2e, ui verification, baseline test, authenticated ui test, test auth, dev login as, dev auth bypass
|
|
32
32
|
- `ui-testing.md`
|
|
33
|
-
-
|
|
34
|
-
- `
|
|
33
|
+
- UI contract, design contract, navigation roles, density, placeholder copy, card shells, shared CRUD screens, row actions, synthetic rows, detail slots
|
|
34
|
+
- `ui-contract.md`
|
|
35
35
|
- filter, filters, search facets, chips, date range, enum filter, lookup filter, `useCrudListFilters`, `createCrudListFilters`
|
|
36
36
|
- `filters.md`
|
|
37
37
|
- `searchSchema`, `search: true`, `applyFilter`, server search, query validators, backend filters, internal JSON REST filters
|
|
@@ -47,13 +47,13 @@ How to use it:
|
|
|
47
47
|
- [surfaces.md](./surfaces.md)
|
|
48
48
|
- [page-scaffolding.md](./page-scaffolding.md)
|
|
49
49
|
- [child-cruds.md](./child-cruds.md)
|
|
50
|
-
- [crud-
|
|
50
|
+
- [crud-authoring.md](./crud-authoring.md)
|
|
51
51
|
- [crud-links.md](./crud-links.md)
|
|
52
52
|
- [page-redirects.md](./page-redirects.md)
|
|
53
53
|
- [live-actions.md](./live-actions.md)
|
|
54
54
|
- [client-requests.md](./client-requests.md)
|
|
55
55
|
- [ui-testing.md](./ui-testing.md)
|
|
56
|
-
- [
|
|
56
|
+
- [ui-contract.md](./ui-contract.md)
|
|
57
57
|
- [filters.md](./filters.md)
|
|
58
58
|
- [server-search.md](./server-search.md)
|
|
59
59
|
- [crud-repository-mapping.md](./crud-repository-mapping.md)
|
package/patterns/child-cruds.md
CHANGED
|
@@ -8,7 +8,7 @@ Use when:
|
|
|
8
8
|
|
|
9
9
|
Rules:
|
|
10
10
|
|
|
11
|
-
- Before
|
|
11
|
+
- Before authoring a child CRUD, ask how the user wants the child records laid out.
|
|
12
12
|
- Do not assume one layout pattern by default.
|
|
13
13
|
|
|
14
14
|
Clarify these options:
|
|
@@ -22,9 +22,9 @@ Clarify these options:
|
|
|
22
22
|
|
|
23
23
|
Why this matters:
|
|
24
24
|
|
|
25
|
-
- the answer changes route structure, placements, host containers, and which
|
|
25
|
+
- the answer changes route structure, placements, host containers, and which source pattern fits best
|
|
26
26
|
- child CRUD layout mistakes are expensive to unwind later
|
|
27
27
|
|
|
28
28
|
Avoid:
|
|
29
29
|
|
|
30
|
-
-
|
|
30
|
+
- authoring nested CRUD routes before the parent/child layout is agreed
|
|
@@ -19,7 +19,7 @@ Rules:
|
|
|
19
19
|
Choose the function like this:
|
|
20
20
|
|
|
21
21
|
```js
|
|
22
|
-
// 1.
|
|
22
|
+
// 1. Standard CRUD route screen
|
|
23
23
|
const screen = useCrudListScreen({ ... });
|
|
24
24
|
|
|
25
25
|
// 2. Button/toggle/small mutation
|
|
@@ -38,15 +38,15 @@ const form = useAddEdit({ ... });
|
|
|
38
38
|
const resource = useEndpointResource({ ... });
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
Use the shared CRUD screen wrappers when the route is a
|
|
41
|
+
Use the shared CRUD screen wrappers when the route is a standard CRUD page:
|
|
42
42
|
|
|
43
43
|
- `useCrudListScreen()` plus `CrudListScreen` for list route pages
|
|
44
44
|
- `useCrudViewScreen()` plus `CrudViewScreen` for record view route pages
|
|
45
|
-
- `useCrudAddEditScreen()` plus `CrudAddEditScreen` for
|
|
45
|
+
- `useCrudAddEditScreen()` plus `CrudAddEditScreen` for new/edit route pages
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
Screen wrapper extension rules:
|
|
48
48
|
|
|
49
|
-
- Use `useCrudListScreen({ readEnabled })` for permission-gated
|
|
49
|
+
- Use `useCrudListScreen({ readEnabled })` for permission-gated 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
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.
|
|
@@ -73,7 +73,7 @@ Why this is the standard JSKIT shape:
|
|
|
73
73
|
- `httpWebClient` already handles credentials and CSRF behavior.
|
|
74
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.
|
|
75
75
|
- Use `requestQueryParams` for endpoint query strings on list, view, and add/edit runtimes.
|
|
76
|
-
-
|
|
76
|
+
- Standard CRUD and lookup reads use all resource-defined output fields by default. Hydrated relationships use the target resource's output contract. Pages and lookup controls do not repeat those definitions as request fieldsets.
|
|
77
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
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
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.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# CRUD authoring
|
|
2
|
+
|
|
3
|
+
Use this file for normal persisted entities, resource contracts, migrations,
|
|
4
|
+
JSON API routes, and matching screens.
|
|
5
|
+
|
|
6
|
+
## Start from product decisions
|
|
7
|
+
|
|
8
|
+
Establish the entity, fields, relationships, ownership, access surface,
|
|
9
|
+
operations, search, sorting, and destructive behavior. Ask when a material
|
|
10
|
+
choice is missing. Do not turn these decisions into generator options.
|
|
11
|
+
|
|
12
|
+
Read these package-owned source patterns:
|
|
13
|
+
|
|
14
|
+
- `crud/resource-contract` for the shared field and ownership contract
|
|
15
|
+
- `crud/json-api-resource-package` for migration and server composition
|
|
16
|
+
- `crud/crud-screen-set` for routed list, view, create, edit, and delete UI
|
|
17
|
+
|
|
18
|
+
## Normal implementation order
|
|
19
|
+
|
|
20
|
+
1. Author a source-controlled migration in the package that owns the entity.
|
|
21
|
+
2. Define the resource with `defineCrudResource()`.
|
|
22
|
+
3. Bind standard server behavior with `defineCrudJsonApiFeature()`.
|
|
23
|
+
4. Add only product-specific service methods, lifecycle, actions, validation,
|
|
24
|
+
policy, queries, messages, and orchestration.
|
|
25
|
+
5. Declare the package provider and framework capabilities in `package.json`.
|
|
26
|
+
6. Build thin route pages over JSKIT's shared CRUD screen APIs.
|
|
27
|
+
7. Install the coherent package graph once.
|
|
28
|
+
8. Rebuild a disposable database and run focused server, client, and browser
|
|
29
|
+
verification.
|
|
30
|
+
|
|
31
|
+
The migration and resource are both authored contracts. A live schema is useful
|
|
32
|
+
for understanding an imported system, but it is never the sole source of truth.
|
|
33
|
+
|
|
34
|
+
## Ownership and access
|
|
35
|
+
|
|
36
|
+
- `public` means every record is intentionally public. It cannot be combined
|
|
37
|
+
with user or workspace ownership.
|
|
38
|
+
- `user` requires a real user ownership column and negative cross-user tests.
|
|
39
|
+
- `workspace` and `workspace_user` require an explicit workspace route/action
|
|
40
|
+
scope plus named permission policy.
|
|
41
|
+
- Domain relationships such as `recipient_user_id` are not ownership merely
|
|
42
|
+
because they point to a user.
|
|
43
|
+
|
|
44
|
+
Normal CRUD tables use one non-null single-column primary key. Foreign keys are
|
|
45
|
+
single-column and target compatible keys. Composite unique indexes may enforce
|
|
46
|
+
business rules but do not become framework identities.
|
|
47
|
+
|
|
48
|
+
## Schema evolution
|
|
49
|
+
|
|
50
|
+
An unapplied migration may be edited normally. Once applied or released, add a
|
|
51
|
+
new immutable migration for the next change. Keep resource fields and storage
|
|
52
|
+
metadata aligned with the resulting schema. Do not use a field-patching command
|
|
53
|
+
or rewrite a historical baseline.
|
|
54
|
+
|
|
55
|
+
## Customize without copying CRUD
|
|
56
|
+
|
|
57
|
+
`defineCrudJsonApiFeature()` deliberately keeps application extension points:
|
|
58
|
+
|
|
59
|
+
- `decorateRepository` adds the few persistence operations unique to the
|
|
60
|
+
resource.
|
|
61
|
+
- `decorateService` overrides standard methods or adds domain methods such as
|
|
62
|
+
`confirm`, `publish`, `cancel`, or `sendReminder`.
|
|
63
|
+
- `operationLifecycle` wraps a standard operation with `before`, `execute`,
|
|
64
|
+
`after`, and mutation-only `afterCommit` phases. Mutation phases before commit
|
|
65
|
+
receive the same transaction and `execute` receives `standard(nextInput)`.
|
|
66
|
+
- `actions` exposes non-CRUD service methods through normal validated,
|
|
67
|
+
permissioned, audited JSKIT actions and optional explicit HTTP routes.
|
|
68
|
+
|
|
69
|
+
Keep database reads and writes in repositories. Services and lifecycle hooks
|
|
70
|
+
orchestrate repositories; they do not issue raw database queries. Put external
|
|
71
|
+
side effects after commit, or write a durable outbox record inside the
|
|
72
|
+
transaction when delivery must be reliable.
|
|
73
|
+
|
|
74
|
+
## When the resource abstraction does not fit
|
|
75
|
+
|
|
76
|
+
Use a separate explicit Feature for a different domain, aggregate, import job,
|
|
77
|
+
or command-oriented workflow that merely happens to mention the resource. A
|
|
78
|
+
CRUD resource may have many custom operations; it stops fitting only when CRUD
|
|
79
|
+
is no longer its principal public contract.
|
|
80
|
+
|
|
81
|
+
## Verification
|
|
82
|
+
|
|
83
|
+
- rebuild from zero in a fresh disposable database
|
|
84
|
+
- validate accepted and rejected values at the resource boundary
|
|
85
|
+
- test list/view/create/update/delete document shapes
|
|
86
|
+
- test positive owner access and negative cross-owner access
|
|
87
|
+
- test relationships and deletion behavior
|
|
88
|
+
- verify skeletons, stable pending labels, toast mutation feedback, and no
|
|
89
|
+
horizontal overflow in compact, medium, and expanded screens
|
|
90
|
+
- run current framework and application verification
|
|
91
|
+
|
|
92
|
+
Avoid generator provenance, scaffold shapes, ownership receipts, workboards,
|
|
93
|
+
operation ledgers, hidden file markers, live-table-only schemas, and duplicated
|
|
94
|
+
route/request validators.
|
package/patterns/crud-links.md
CHANGED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: server/feature-package
|
|
3
|
+
title: App-local server feature package
|
|
4
|
+
summary: Define a server feature through explicit capabilities and first-class actions, adding services or repositories only when its domain earns them.
|
|
5
|
+
keywords: actions, feature, json-rest, knex, orchestration, package, provider, repository, routes, server
|
|
6
|
+
requires: @jskit-ai/kernel
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# App-local server feature package
|
|
10
|
+
|
|
11
|
+
## Use when
|
|
12
|
+
|
|
13
|
+
Use this pattern when a server capability has its own product language and
|
|
14
|
+
should not grow inside `packages/main`. It shows the smallest normal shape: a
|
|
15
|
+
feature declaration, named capability inputs and outputs, and first-class
|
|
16
|
+
actions whose implementations capture those exact dependencies.
|
|
17
|
+
|
|
18
|
+
The concrete `booking-engine` example is intentionally repository-free. The
|
|
19
|
+
`variations/` directory preserves the two important escalations: a meaningful
|
|
20
|
+
orchestration service and an explicit custom Knex repository for a domain that
|
|
21
|
+
cannot fit the normal resource APIs.
|
|
22
|
+
|
|
23
|
+
## Do not use when
|
|
24
|
+
|
|
25
|
+
Do not create a package for a tiny composition callback or one route with no
|
|
26
|
+
domain behavior. Use the CRUD resource patterns when ordinary resource
|
|
27
|
+
operations fit. Do not select custom Knex merely because SQL is familiar; it
|
|
28
|
+
is an explicit exception for persistence behavior the higher-level APIs cannot
|
|
29
|
+
express.
|
|
30
|
+
|
|
31
|
+
## Product decisions
|
|
32
|
+
|
|
33
|
+
Decide the feature's public operations, access surface, authentication policy,
|
|
34
|
+
input and output contracts, transactional boundary, and whether it truly owns
|
|
35
|
+
persistence. Decide whether HTTP routes are needed or another package will call
|
|
36
|
+
its actions internally. These decisions come from the product, not a JSKIT
|
|
37
|
+
questionnaire.
|
|
38
|
+
|
|
39
|
+
## Invariants
|
|
40
|
+
|
|
41
|
+
- `packages/main` composes the application; substantial feature logic lives in
|
|
42
|
+
the feature package.
|
|
43
|
+
- `defineFeature()` receives only declared capabilities and returns only
|
|
44
|
+
declared feature APIs.
|
|
45
|
+
- Actions define the public operation contract and close over the feature APIs
|
|
46
|
+
they need. They never resolve arbitrary dependencies at execution time.
|
|
47
|
+
- The service orchestrates the feature and does not issue SQL directly.
|
|
48
|
+
- A repository owns persistence and accepts transaction/context options.
|
|
49
|
+
- The normal persistence lane uses a public or documented high-level resource
|
|
50
|
+
API; custom Knex is an explicit, reviewed exception.
|
|
51
|
+
- Ordinary HTTP and assistant exposure is projected from actions. Explicit
|
|
52
|
+
route code exists only for a genuinely custom transport.
|
|
53
|
+
- Register routes, actions, contributors, and other catalogue entries during
|
|
54
|
+
`setup()`. Runtime catalogues may be sealed before provider `boot()` begins.
|
|
55
|
+
Reserve `boot()` for starting long-lived work such as consumers, schedulers,
|
|
56
|
+
or notification loops after the application structure is complete.
|
|
57
|
+
- Application source contains no scaffold-shape, lane, provenance, receipt, or
|
|
58
|
+
authoring-history metadata.
|
|
59
|
+
|
|
60
|
+
## Framework APIs
|
|
61
|
+
|
|
62
|
+
The provider uses `defineFeature()` from
|
|
63
|
+
`@jskit-ai/kernel/server/features`. The runtime supplies named capabilities
|
|
64
|
+
and the feature publishes its actions through the dedicated action catalogue.
|
|
65
|
+
The custom persistence variation uses `createWithTransaction()` from
|
|
66
|
+
`@jskit-ai/database-runtime/shared`.
|
|
67
|
+
|
|
68
|
+
## Example files
|
|
69
|
+
|
|
70
|
+
`example/booking-engine/` is a concrete app-local package showing the preferred
|
|
71
|
+
small-feature boundary. Its status query is deliberately small: product logic
|
|
72
|
+
can grow inside the returned feature API without creating ceremonial layers.
|
|
73
|
+
|
|
74
|
+
`example/variations/AvailabilityEngineProvider.js` and
|
|
75
|
+
`example/variations/orchestratorService.js` show the repository-free lane.
|
|
76
|
+
`example/variations/InvoiceRollupProvider.js` and
|
|
77
|
+
`example/variations/customKnexRepository.js` show the explicit custom
|
|
78
|
+
persistence lane.
|
|
79
|
+
|
|
80
|
+
## Variation points
|
|
81
|
+
|
|
82
|
+
Rename the package, capability id, actions, validators, and domain methods.
|
|
83
|
+
Select the correct channels and surfaces in `actionDefaults`, overriding them
|
|
84
|
+
only for exceptional actions. Add a service when orchestration is shared or
|
|
85
|
+
stateful. Add a repository only when the feature owns persistence. Replace the
|
|
86
|
+
illustrative status query with narrow product language before shipping.
|
|
87
|
+
|
|
88
|
+
## Verification
|
|
89
|
+
|
|
90
|
+
- Import the feature and verify its declared capability inputs and outputs.
|
|
91
|
+
- Exercise actions through the action runtime, including invalid input and
|
|
92
|
+
denied access.
|
|
93
|
+
- Exercise HTTP routes only when the package owns them.
|
|
94
|
+
- Test service orchestration independently from persistence.
|
|
95
|
+
- Test repository transaction and context forwarding.
|
|
96
|
+
- Rebuild any schema from immutable migrations in a disposable database.
|
|
97
|
+
- Run the app's package, server, and integration verification.
|
|
98
|
+
|
|
99
|
+
## Avoid
|
|
100
|
+
|
|
101
|
+
- product logic in the provider or `packages/main`
|
|
102
|
+
- registering routes or action catalogue entries from `boot()`
|
|
103
|
+
- direct SQL in actions, routes, or services
|
|
104
|
+
- a repository for an orchestration-only feature
|
|
105
|
+
- generic `execute(anything)` operations in finished product code
|
|
106
|
+
- `app.make()`, `scope.make()`, container tokens, or service-location helpers
|
|
107
|
+
- metadata claiming which authoring tool or pattern created the package
|
|
108
|
+
- field questionnaires, overwrite switches, or durable operation records
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@local/booking-engine",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "App-local booking orchestration package.",
|
|
5
|
+
"private": true,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@jskit-ai/kernel": "0.1.161",
|
|
9
|
+
"json-rest-schema": "^1.0.17"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
"./server/actions": "./src/server/actions.js"
|
|
13
|
+
},
|
|
14
|
+
"jskit": {
|
|
15
|
+
"kind": "runtime",
|
|
16
|
+
"capabilities": {
|
|
17
|
+
"provides": [
|
|
18
|
+
"feature.booking-engine"
|
|
19
|
+
],
|
|
20
|
+
"requires": [
|
|
21
|
+
"runtime.actions"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"runtime": {
|
|
25
|
+
"server": {
|
|
26
|
+
"providers": [
|
|
27
|
+
{
|
|
28
|
+
"entrypoint": "src/server/BookingEngineProvider.js",
|
|
29
|
+
"export": "BookingEngineProvider"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"client": {
|
|
34
|
+
"providers": []
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"metadata": {
|
|
38
|
+
"apiSummary": {
|
|
39
|
+
"surfaces": [
|
|
40
|
+
{
|
|
41
|
+
"subpath": "./server/actions",
|
|
42
|
+
"summary": "Exports booking-engine action definitions."
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { defineFeature } from "@jskit-ai/kernel/server/features";
|
|
2
|
+
import { createActions } from "./actions.js";
|
|
3
|
+
|
|
4
|
+
const BookingEngineProvider = defineFeature({
|
|
5
|
+
id: "feature.booking-engine",
|
|
6
|
+
domain: "booking",
|
|
7
|
+
provides: {
|
|
8
|
+
bookingEngine: "feature.booking-engine"
|
|
9
|
+
},
|
|
10
|
+
actionDefaults: {
|
|
11
|
+
channels: ["api", "assistant", "internal"],
|
|
12
|
+
surfaces: ["app"]
|
|
13
|
+
},
|
|
14
|
+
setup() {
|
|
15
|
+
return {
|
|
16
|
+
bookingEngine: Object.freeze({
|
|
17
|
+
async readStatus(input = {}) {
|
|
18
|
+
return {
|
|
19
|
+
ok: true,
|
|
20
|
+
feature: "booking-engine",
|
|
21
|
+
scope: input.scope || "default",
|
|
22
|
+
verbose: input.verbose === true
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
actions({ bookingEngine }) {
|
|
29
|
+
return createActions({ bookingEngine });
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export { BookingEngineProvider };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { statusQueryInputValidator } from "./inputSchemas.js";
|
|
2
|
+
|
|
3
|
+
const ACTION_GET_STATUS = "feature.booking-engine.status.read";
|
|
4
|
+
function createActions({ bookingEngine } = {}) {
|
|
5
|
+
if (!bookingEngine || typeof bookingEngine.readStatus !== "function") {
|
|
6
|
+
throw new TypeError("createActions requires bookingEngine.readStatus().");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
return Object.freeze([
|
|
10
|
+
{
|
|
11
|
+
id: ACTION_GET_STATUS,
|
|
12
|
+
version: 1,
|
|
13
|
+
kind: "query",
|
|
14
|
+
input: statusQueryInputValidator,
|
|
15
|
+
output: null,
|
|
16
|
+
idempotency: "none",
|
|
17
|
+
audit: { actionName: ACTION_GET_STATUS },
|
|
18
|
+
observability: {},
|
|
19
|
+
async execute(input) {
|
|
20
|
+
return bookingEngine.readStatus(input);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
]);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { ACTION_GET_STATUS, createActions };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { deepFreeze } from "@jskit-ai/kernel/shared/support/deepFreeze";
|
|
2
|
+
import { createSchema } from "json-rest-schema";
|
|
3
|
+
|
|
4
|
+
const statusQueryInputValidator = deepFreeze({
|
|
5
|
+
schema: createSchema({
|
|
6
|
+
scope: {
|
|
7
|
+
type: "string",
|
|
8
|
+
required: false,
|
|
9
|
+
minLength: 1
|
|
10
|
+
},
|
|
11
|
+
verbose: {
|
|
12
|
+
type: "boolean",
|
|
13
|
+
required: false
|
|
14
|
+
}
|
|
15
|
+
}),
|
|
16
|
+
mode: "patch"
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export { statusQueryInputValidator };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { defineFeature } from "@jskit-ai/kernel/server/features";
|
|
2
|
+
import { emptyInputValidator } from "@jskit-ai/kernel/shared/actions/actionContributorHelpers";
|
|
3
|
+
import { createService } from "./orchestratorService.js";
|
|
4
|
+
|
|
5
|
+
const AvailabilityEngineProvider = defineFeature({
|
|
6
|
+
id: "feature.availability-engine",
|
|
7
|
+
domain: "availability",
|
|
8
|
+
provides: {
|
|
9
|
+
availabilityEngine: "feature.availability-engine"
|
|
10
|
+
},
|
|
11
|
+
actionDefaults: {
|
|
12
|
+
channels: ["api", "assistant", "internal"],
|
|
13
|
+
surfaces: ["app"]
|
|
14
|
+
},
|
|
15
|
+
setup() {
|
|
16
|
+
return { availabilityEngine: createService() };
|
|
17
|
+
},
|
|
18
|
+
actions({ availabilityEngine }) {
|
|
19
|
+
return [{
|
|
20
|
+
id: "availability.status.read",
|
|
21
|
+
kind: "query",
|
|
22
|
+
input: emptyInputValidator,
|
|
23
|
+
idempotency: "none",
|
|
24
|
+
async execute(input) {
|
|
25
|
+
return availabilityEngine.getStatus(input);
|
|
26
|
+
}
|
|
27
|
+
}];
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export { AvailabilityEngineProvider };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { defineFeature } from "@jskit-ai/kernel/server/features";
|
|
2
|
+
import { emptyInputValidator } from "@jskit-ai/kernel/shared/actions/actionContributorHelpers";
|
|
3
|
+
import { createRepository } from "./repository.js";
|
|
4
|
+
|
|
5
|
+
const InvoiceRollupProvider = defineFeature({
|
|
6
|
+
id: "feature.invoice-rollup",
|
|
7
|
+
domain: "invoices",
|
|
8
|
+
requires: {
|
|
9
|
+
database: "runtime.database"
|
|
10
|
+
},
|
|
11
|
+
provides: {
|
|
12
|
+
invoiceRollup: "feature.invoice-rollup"
|
|
13
|
+
},
|
|
14
|
+
actionDefaults: {
|
|
15
|
+
channels: ["api", "assistant", "internal"],
|
|
16
|
+
surfaces: ["admin"]
|
|
17
|
+
},
|
|
18
|
+
setup({ database }) {
|
|
19
|
+
return {
|
|
20
|
+
invoiceRollup: createRepository({ knex: database.knex })
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
actions({ invoiceRollup }) {
|
|
24
|
+
return [{
|
|
25
|
+
id: "invoices.rollup.status.read",
|
|
26
|
+
kind: "query",
|
|
27
|
+
input: emptyInputValidator,
|
|
28
|
+
idempotency: "none",
|
|
29
|
+
async execute(input, context) {
|
|
30
|
+
return invoiceRollup.getStatus(input, { context });
|
|
31
|
+
}
|
|
32
|
+
}];
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export { InvoiceRollupProvider };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createWithTransaction } from "@jskit-ai/database-runtime/shared";
|
|
2
|
+
|
|
3
|
+
function createRepository({ knex } = {}) {
|
|
4
|
+
if (!knex) {
|
|
5
|
+
throw new TypeError("createRepository requires knex.");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return Object.freeze({
|
|
9
|
+
withTransaction: createWithTransaction(knex),
|
|
10
|
+
async getStatus(input = {}, options = {}) {
|
|
11
|
+
return {
|
|
12
|
+
ok: true,
|
|
13
|
+
feature: "invoice-rollup",
|
|
14
|
+
persistence: "custom-knex",
|
|
15
|
+
tableName: "invoice_rollups",
|
|
16
|
+
hasTransaction: Boolean(options.trx),
|
|
17
|
+
input
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
async execute(input = {}, options = {}) {
|
|
21
|
+
return {
|
|
22
|
+
accepted: false,
|
|
23
|
+
feature: "invoice-rollup",
|
|
24
|
+
persistence: "custom-knex",
|
|
25
|
+
tableName: "invoice_rollups",
|
|
26
|
+
hasTransaction: Boolean(options.trx),
|
|
27
|
+
input,
|
|
28
|
+
message: "Replace this example with the reviewed domain-specific query."
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { createRepository };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
function createService() {
|
|
2
|
+
return Object.freeze({
|
|
3
|
+
async getStatus(input = {}) {
|
|
4
|
+
return {
|
|
5
|
+
ok: true,
|
|
6
|
+
feature: "availability-engine",
|
|
7
|
+
mode: "orchestrator",
|
|
8
|
+
input
|
|
9
|
+
};
|
|
10
|
+
},
|
|
11
|
+
async execute(input = {}) {
|
|
12
|
+
return {
|
|
13
|
+
accepted: false,
|
|
14
|
+
feature: "availability-engine",
|
|
15
|
+
mode: "orchestrator",
|
|
16
|
+
input,
|
|
17
|
+
message: "Replace this example with calls to the feature's injected collaborators."
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { createService };
|