@jskit-ai/agent-docs 0.1.257 → 0.1.258
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-conversation.md +88 -35
- package/guide/agent/app-extras/assistant.md +28 -28
- package/guide/agent/app-setup/authentication.md +36 -17
- package/guide/agent/app-setup/database-layer.md +116 -17
- package/guide/agent/app-setup/existing-application-migration.md +29 -25
- package/guide/agent/app-setup/multi-homing.md +94 -48
- package/guide/agent/app-setup/run-an-accounts-hub.md +6 -4
- package/guide/agent/framework/client-features.md +496 -0
- package/guide/agent/framework/crud-operations.md +5 -5
- package/guide/agent/framework/translation.md +4 -3
- package/guide/agent/framework/typed-features.md +1104 -0
- package/guide/agent/framework/ui-operations.md +2 -2
- package/guide/agent/index.md +6 -1
- package/package.json +1 -1
- package/patterns/feature-package/PATTERN.md +104 -10
- package/patterns/feature-package/example/booking-engine/package.json +9 -4
- package/patterns/feature-package/example/booking-engine/src/server/BookingEngineFeature.js +24 -0
- package/patterns/feature-package/example/booking-engine/src/server/bookingAvailability.js +10 -0
- package/patterns/feature-package/example/booking-engine/src/server/bookingEngine.js +29 -0
- package/patterns/feature-package/example/booking-engine/src/server/bookingEngineActions.js +26 -0
- package/patterns/feature-package/example/booking-engine/src/server/inputSchemas.js +1 -2
- package/patterns/feature-package/example/booking-engine/src/server/types.js +12 -0
- package/patterns/feature-package/example/variations/AvailabilityEngineFeature.js +19 -0
- package/patterns/feature-package/example/variations/InvoiceRollupFeature.js +23 -0
- package/patterns/feature-package/example/variations/availabilityEngineActions.js +26 -0
- package/patterns/feature-package/example/variations/customKnexRepository.js +32 -26
- package/patterns/feature-package/example/variations/invoiceRollupActions.js +26 -0
- package/patterns/feature-package/example/variations/orchestratorService.js +24 -19
- package/patterns/feature-package/example/variations/types.js +7 -0
- package/patterns/minimal-foundation/PATTERN.md +30 -1
- package/patterns/minimal-foundation/example/AGENTS.md +14 -0
- package/patterns/minimal-foundation/example/bin/develop.js +10 -5
- package/patterns/minimal-foundation/example/eslint.config.mjs +21 -2
- package/patterns/minimal-foundation/example/package.json +5 -4
- package/patterns/minimal-foundation/example/playwright.config.mjs +1 -1
- package/patterns/minimal-foundation/example/server/lib/envFile.js +19 -0
- package/patterns/minimal-foundation/example/server/lib/loggerOptions.js +15 -0
- package/patterns/minimal-foundation/example/server/lib/runtimeEnv.js +17 -17
- package/patterns/minimal-foundation/example/server.js +32 -29
- package/patterns/minimal-foundation/example/tests/server/smoke.test.js +2 -1
- package/patterns/shell-foundation/PATTERN.md +29 -0
- package/patterns/shell-foundation/example/AGENTS.md +14 -0
- package/patterns/shell-foundation/example/bin/develop.js +10 -5
- package/patterns/shell-foundation/example/eslint.config.mjs +21 -2
- package/patterns/shell-foundation/example/package.json +6 -5
- package/patterns/shell-foundation/example/packages/main/package.json +1 -1
- package/patterns/shell-foundation/example/packages/main/src/client/providers/MainClientProvider.js +1 -0
- package/patterns/shell-foundation/example/playwright.config.mjs +1 -1
- package/patterns/shell-foundation/example/server/lib/envFile.js +19 -0
- package/patterns/shell-foundation/example/server/lib/loggerOptions.js +15 -0
- package/patterns/shell-foundation/example/server/lib/runtimeEnv.js +17 -17
- package/patterns/shell-foundation/example/server.js +32 -29
- package/patterns/shell-foundation/example/tests/server/smoke.test.js +2 -1
- package/reference/autogen/KERNEL_MAP.md +14 -8
- package/reference/autogen/PATTERN_INDEX.md +37 -37
- package/reference/autogen/README.md +2 -0
- package/reference/autogen/packages/agent-docs.md +70 -26
- package/reference/autogen/packages/agent-runtime.md +129 -0
- package/reference/autogen/packages/agent.md +149 -0
- package/reference/autogen/packages/architecture-inspector.md +97 -44
- package/reference/autogen/packages/assistant-core.md +786 -292
- package/reference/autogen/packages/assistant-runtime.md +182 -57
- package/reference/autogen/packages/assistant-voice-sherpa.md +4 -0
- package/reference/autogen/packages/assistant-voice.md +54 -8
- package/reference/autogen/packages/auth-core.md +44 -13
- package/reference/autogen/packages/auth-provider-local-core.md +86 -34
- package/reference/autogen/packages/auth-provider-local-db-core.md +1 -2
- package/reference/autogen/packages/auth-provider-oidc-core.md +10 -4
- package/reference/autogen/packages/auth-provider-supabase-core.md +52 -66
- package/reference/autogen/packages/auth-web.md +19 -11
- package/reference/autogen/packages/connectors-catalog.md +71 -43
- package/reference/autogen/packages/connectors-core.md +42 -4
- package/reference/autogen/packages/console-core.md +27 -14
- package/reference/autogen/packages/crud-core.md +178 -170
- package/reference/autogen/packages/database-runtime-mysql.md +0 -2
- package/reference/autogen/packages/database-runtime-postgres.md +0 -2
- package/reference/autogen/packages/database-runtime.md +39 -10
- package/reference/autogen/packages/http-runtime.md +79 -57
- package/reference/autogen/packages/json-rest-api-core.md +11 -8
- package/reference/autogen/packages/kernel.md +144 -83
- package/reference/autogen/packages/oidc-issuer-core.md +49 -20
- package/reference/autogen/packages/payments-core.md +20 -3
- package/reference/autogen/packages/realtime.md +47 -22
- package/reference/autogen/packages/resource-core.md +1 -1
- package/reference/autogen/packages/resource-crud-core.md +64 -26
- package/reference/autogen/packages/rewarded-core.md +47 -36
- package/reference/autogen/packages/shell-web.md +7 -10
- package/reference/autogen/packages/storage-runtime.md +6 -2
- package/reference/autogen/packages/uploads-runtime.md +10 -2
- package/reference/autogen/packages/users-core.md +73 -71
- package/reference/autogen/packages/workspaces-core.md +121 -106
- package/reference/autogen/packages/workspaces-web.md +1 -2
- package/reference/autogen/tooling/config-eslint.md +302 -0
- package/reference/autogen/tooling/jskit-catalog.md +28 -18
- package/reference/autogen/tooling/jskit-release.md +35 -20
- package/reference/autogen/tooling/testUtils.md +3 -1
- package/skills/jskit/SKILL.md +3 -2
- package/skills/jskit/references/app-operations.md +1 -1
- package/skills/jskit/references/client-features.md +491 -0
- package/skills/jskit/references/crud-form-actions.md +1 -1
- package/skills/jskit/references/crud-operations.md +7 -7
- package/skills/jskit/references/existing-application-migration.md +31 -27
- package/skills/jskit/references/heuristics/INDEX.md +5 -1
- package/skills/jskit/references/heuristics/child-cruds.md +1 -1
- package/skills/jskit/references/heuristics/client-features.md +66 -0
- package/skills/jskit/references/heuristics/client-requests.md +1 -1
- package/skills/jskit/references/heuristics/crud-authoring.md +12 -2
- package/skills/jskit/references/heuristics/crud-links.md +1 -1
- package/skills/jskit/references/heuristics/crud-repository-mapping.md +3 -3
- package/skills/jskit/references/heuristics/filters.md +1 -1
- package/skills/jskit/references/heuristics/live-actions.md +1 -1
- package/skills/jskit/references/heuristics/page-redirects.md +1 -1
- package/skills/jskit/references/heuristics/page-scaffolding.md +1 -1
- package/skills/jskit/references/heuristics/placements.md +1 -1
- package/skills/jskit/references/heuristics/row-policies.md +1 -1
- package/skills/jskit/references/heuristics/server-search.md +1 -1
- package/skills/jskit/references/heuristics/surfaces.md +1 -1
- package/skills/jskit/references/heuristics/typed-features.md +100 -0
- package/skills/jskit/references/heuristics/ui-contract.md +1 -1
- package/skills/jskit/references/heuristics/ui-testing.md +4 -4
- package/skills/jskit/references/material-3.md +2 -2
- package/skills/jskit/references/multi-homing.md +96 -50
- package/skills/jskit/references/pattern-index.md +37 -37
- package/skills/jskit/references/patterns/app/minimal-foundation/PATTERN.md +30 -1
- package/skills/jskit/references/patterns/app/minimal-foundation/example/AGENTS.md +14 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/bin/develop.js +10 -5
- package/skills/jskit/references/patterns/app/minimal-foundation/example/eslint.config.mjs +21 -2
- package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +5 -4
- package/skills/jskit/references/patterns/app/minimal-foundation/example/playwright.config.mjs +1 -1
- package/skills/jskit/references/patterns/app/minimal-foundation/example/server/lib/envFile.js +19 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/server/lib/loggerOptions.js +15 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/server/lib/runtimeEnv.js +17 -17
- package/skills/jskit/references/patterns/app/minimal-foundation/example/server.js +32 -29
- package/skills/jskit/references/patterns/app/minimal-foundation/example/tests/server/smoke.test.js +2 -1
- package/skills/jskit/references/patterns/app/shell-foundation/PATTERN.md +29 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/AGENTS.md +14 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/bin/develop.js +10 -5
- package/skills/jskit/references/patterns/app/shell-foundation/example/eslint.config.mjs +21 -2
- package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +6 -5
- package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/package.json +1 -1
- package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/src/client/providers/MainClientProvider.js +1 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/playwright.config.mjs +1 -1
- package/skills/jskit/references/patterns/app/shell-foundation/example/server/lib/envFile.js +19 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/server/lib/loggerOptions.js +15 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/server/lib/runtimeEnv.js +17 -17
- package/skills/jskit/references/patterns/app/shell-foundation/example/server.js +32 -29
- package/skills/jskit/references/patterns/app/shell-foundation/example/tests/server/smoke.test.js +2 -1
- package/skills/jskit/references/patterns/assistant/assistant-surface/example/src/ApplicationAiFeature.js +4 -2
- package/skills/jskit/references/patterns/auth/accounts-hub/PATTERN.md +2 -1
- package/skills/jskit/references/patterns/auth/accounts-hub/example/package.json +2 -2
- package/skills/jskit/references/patterns/auth/accounts-hub/example/server/hubPaths.js +9 -3
- package/skills/jskit/references/patterns/auth/auth-surface/PATTERN.md +25 -12
- package/skills/jskit/references/patterns/auth/auth-surface/example/tools/preview-identity +7 -1
- package/skills/jskit/references/patterns/auth/oidc-sign-in/example/package.json +2 -2
- package/skills/jskit/references/patterns/auth/supabase-auth/example/package.json +1 -1
- package/skills/jskit/references/patterns/connectors/ai-connections/PATTERN.md +16 -1
- package/skills/jskit/references/patterns/connectors/ai-connections/example/ai-model.js +12 -3
- package/skills/jskit/references/patterns/connectors/ai-connections/example/perplexity-answer.js +15 -4
- package/skills/jskit/references/patterns/connectors/api-key-connection/PATTERN.md +9 -1
- package/skills/jskit/references/patterns/connectors/api-key-connection/example/connections.js +5 -2
- package/skills/jskit/references/patterns/connectors/aws-storage-queries/PATTERN.md +13 -3
- package/skills/jskit/references/patterns/connectors/aws-storage-queries/example/s3-transfer.js +14 -3
- package/skills/jskit/references/patterns/connectors/calendar-cli/PATTERN.md +8 -0
- package/skills/jskit/references/patterns/connectors/calendar-cli/example/knexfile.js +1 -1
- package/skills/jskit/references/patterns/connectors/calendar-cli/example/package.json +4 -4
- package/skills/jskit/references/patterns/connectors/calendar-cli/example/scripts/calendar.js +8 -4
- package/skills/jskit/references/patterns/connectors/event-delivery/PATTERN.md +1 -1
- package/skills/jskit/references/patterns/connectors/firebase-messaging/PATTERN.md +10 -0
- package/skills/jskit/references/patterns/connectors/firebase-messaging/example/server/notifications.js +8 -0
- package/skills/jskit/references/patterns/connectors/google-ads-search/PATTERN.md +18 -7
- package/skills/jskit/references/patterns/connectors/google-ads-search/example/ads-setup.js +13 -5
- package/skills/jskit/references/patterns/connectors/oauth-connection/PATTERN.md +1 -1
- package/skills/jskit/references/patterns/connectors/paddle-catalogue/PATTERN.md +11 -2
- package/skills/jskit/references/patterns/connectors/paddle-catalogue/example/create-products.js +5 -2
- package/skills/jskit/references/patterns/connectors/public-image/PATTERN.md +10 -1
- package/skills/jskit/references/patterns/connectors/public-image/example/logo-url.js +4 -1
- package/skills/jskit/references/patterns/connectors/redshift-queries/PATTERN.md +1 -1
- package/skills/jskit/references/patterns/connectors/source-scanning/PATTERN.md +13 -2
- package/skills/jskit/references/patterns/connectors/source-scanning/example/source-scanner.js +9 -0
- package/skills/jskit/references/patterns/crud/json-api-resource-package/PATTERN.md +102 -23
- package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/package.json +3 -2
- package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/src/server/types.js +5 -0
- package/skills/jskit/references/patterns/crud/resource-contract/PATTERN.md +54 -1
- package/skills/jskit/references/patterns/database/mysql-application/PATTERN.md +16 -5
- package/skills/jskit/references/patterns/database/mysql-application/example/knexfile.js +2 -2
- package/skills/jskit/references/patterns/database/mysql-application/example/package.json +1 -1
- package/skills/jskit/references/patterns/database/mysql-application/example/scripts/prepare-database.js +1 -1
- package/skills/jskit/references/patterns/database/postgres-application/PATTERN.md +16 -5
- package/skills/jskit/references/patterns/database/postgres-application/example/knexfile.js +2 -2
- package/skills/jskit/references/patterns/database/postgres-application/example/package.json +1 -1
- package/skills/jskit/references/patterns/database/postgres-application/example/scripts/prepare-database.js +1 -1
- package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +2 -2
- package/skills/jskit/references/patterns/rewards/google-rewarded/example/GoogleRewardedDeliveryProvider.js +1 -1
- package/skills/jskit/references/patterns/rewards/google-rewarded/example/googlePublisherTag.js +5 -11
- package/skills/jskit/references/patterns/server/feature-package/PATTERN.md +104 -10
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/package.json +9 -4
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/BookingEngineFeature.js +24 -0
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/bookingAvailability.js +10 -0
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/bookingEngine.js +29 -0
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/bookingEngineActions.js +26 -0
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/inputSchemas.js +1 -2
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/types.js +12 -0
- package/skills/jskit/references/patterns/server/feature-package/example/variations/AvailabilityEngineFeature.js +19 -0
- package/skills/jskit/references/patterns/server/feature-package/example/variations/InvoiceRollupFeature.js +23 -0
- package/skills/jskit/references/patterns/server/feature-package/example/variations/availabilityEngineActions.js +26 -0
- package/skills/jskit/references/patterns/server/feature-package/example/variations/customKnexRepository.js +32 -26
- package/skills/jskit/references/patterns/server/feature-package/example/variations/invoiceRollupActions.js +26 -0
- package/skills/jskit/references/patterns/server/feature-package/example/variations/orchestratorService.js +24 -19
- package/skills/jskit/references/patterns/server/feature-package/example/variations/types.js +7 -0
- package/skills/jskit/references/patterns/users/user-administration-server/PATTERN.md +16 -1
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +3 -2
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/src/server/types.js +5 -0
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +4 -3
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/src/server/UsersWorkspaceFeature.js +2 -9
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/src/server/types.js +5 -0
- package/skills/jskit/references/patterns/workspaces/workspace-server/PATTERN.md +42 -17
- package/skills/jskit/references/patterns/workspaces/workspace-server/example/packages/main/src/server/WorkspaceInvitationsFeature.js +27 -0
- package/skills/jskit/references/patterns/workspaces/workspace-server/example/packages/main/src/server/email/workspaceInviteEmail.js +11 -10
- package/skills/jskit/references/patterns/workspaces/workspace-server/example/packages/main/src/server/email/workspaceInviteMailer.js +27 -0
- package/skills/jskit/references/translation.md +5 -4
- package/skills/jskit/references/typed-features.md +1099 -0
- package/skills/jskit/references/ui-operations.md +3 -3
- package/patterns/feature-package/example/booking-engine/src/server/BookingEngineProvider.js +0 -33
- package/patterns/feature-package/example/booking-engine/src/server/actions.js +0 -24
- package/patterns/feature-package/example/variations/AvailabilityEngineProvider.js +0 -31
- package/patterns/feature-package/example/variations/InvoiceRollupProvider.js +0 -36
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/BookingEngineProvider.js +0 -33
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/actions.js +0 -24
- package/skills/jskit/references/patterns/server/feature-package/example/variations/AvailabilityEngineProvider.js +0 -31
- package/skills/jskit/references/patterns/server/feature-package/example/variations/InvoiceRollupProvider.js +0 -36
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
<!-- Generated from `packages/agent-docs/site/guide/framework/client-features.md`. -->
|
|
2
|
+
|
|
3
|
+
# Client features
|
|
4
|
+
|
|
5
|
+
Read this page before writing or reviewing JSKIT client code: a composable, a
|
|
6
|
+
Vue component, a client factory or a client provider. It is the client half of
|
|
7
|
+
[typed features](typed-features.md): the same rule, the same
|
|
8
|
+
`readable-check: allow` vocabulary and the same size limits, applied to Vue.
|
|
9
|
+
`clientFeaturesConfig` makes the rules a machine can see errors
|
|
10
|
+
([checks](#checks)); the others are checked by reading.
|
|
11
|
+
|
|
12
|
+
The [client features heuristic](heuristics/client-features.md)
|
|
13
|
+
maps keywords and lint findings to the section that fixes them.
|
|
14
|
+
|
|
15
|
+
## The rule
|
|
16
|
+
|
|
17
|
+
> Every collaborator client code uses arrives through its options, props,
|
|
18
|
+
> injection or provider capabilities, typed where it is received and required,
|
|
19
|
+
> never probed, so each call has one visible destination.
|
|
20
|
+
|
|
21
|
+
- A **collaborator** is an object or function with behaviour that code calls:
|
|
22
|
+
an adapter, an API client, a state object with methods, a callback, a router.
|
|
23
|
+
Plain data (a label, a list, a record) is not one.
|
|
24
|
+
- A **composable** is a `use<Name>()` function that holds Vue state; a **client
|
|
25
|
+
factory** is a `create<Name>()` that holds none.
|
|
26
|
+
|
|
27
|
+
## Simplicity
|
|
28
|
+
|
|
29
|
+
### Simple to reason about
|
|
30
|
+
|
|
31
|
+
**A reader follows each flow top to bottom: plain functions, direct calls,
|
|
32
|
+
names that say what they do.** Satisfying a rule by adding a layer, a
|
|
33
|
+
registry, a pass-through wrapper, meta-programming or a one-use abstraction is
|
|
34
|
+
wrong, and so is splitting a file into many tiny modules to meet a size limit.
|
|
35
|
+
|
|
36
|
+
Signs of over-cleverness: a `create*` that only forwards to another; handlers
|
|
37
|
+
built in a loop or from strings; `Object.fromEntries(… .map(…))` to build an
|
|
38
|
+
API; a `Proxy`; a factory used once; a registry for two entries; a module of
|
|
39
|
+
one three-line helper; files named `utils2`, `part1` or `…Helpers`.
|
|
40
|
+
|
|
41
|
+
Why: the rules exist so code is easy to follow; a contortion that silences one
|
|
42
|
+
defeats it.
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
// good: the contract is required, so the calls are direct
|
|
46
|
+
function resend(messageId) {
|
|
47
|
+
return controller.resend(messageId);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// bad: a computed layer that hides two probes
|
|
51
|
+
const operations = Object.fromEntries(["find", "resend"].map((name) => [name, (...args) => controller.value?.[name]?.(...args)]));
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
When a rule seems to force a contortion, take the direct reading that meets
|
|
55
|
+
its intent, and report the case so the rule can be narrowed.
|
|
56
|
+
|
|
57
|
+
## Types and collaborators
|
|
58
|
+
|
|
59
|
+
### Client types
|
|
60
|
+
|
|
61
|
+
**A package's client types live in `src/client/types.js`, exported as
|
|
62
|
+
`./client/types` when another package uses them: one `@typedef` per value its
|
|
63
|
+
client code hands out or receives across files.** That is a composable's
|
|
64
|
+
result (`ReturnType<typeof useX>`), a client factory's result, and each
|
|
65
|
+
contract an application implements, written once with its optional members
|
|
66
|
+
marked `?`. Kernel capabilities come from `@jskit-ai/kernel/client/types`:
|
|
67
|
+
`ClientComponents`, `ClientVueApp`, `ClientRouter`, `ClientPinia`,
|
|
68
|
+
`ClientQueryClient`, `ClientEnv`, `ClientSurfaceRuntime`, `ClientSurfaceMode`
|
|
69
|
+
and `ClientLogger`.
|
|
70
|
+
|
|
71
|
+
Why: a parameter that names its type sends a reader to one file.
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
// good: src/client/types.js
|
|
75
|
+
/** @typedef {ReturnType<typeof import("./useAttachmentQueue.js").useAttachmentQueue>} AttachmentQueue */
|
|
76
|
+
/**
|
|
77
|
+
* What an application supplies to render a conversation.
|
|
78
|
+
* @typedef {object} ConversationAdapter
|
|
79
|
+
* @property {(text: string) => Promise<void>} send
|
|
80
|
+
* @property {MessageDelivery} [delivery] Present when the application tracks delivery.
|
|
81
|
+
*/
|
|
82
|
+
export {};
|
|
83
|
+
|
|
84
|
+
// bad: the shape is whatever the caller passed
|
|
85
|
+
function useConversationActions({ adapter }) {
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Typed options
|
|
89
|
+
|
|
90
|
+
**A composable, client factory or helper destructures its options in its
|
|
91
|
+
signature and types them with a JSDoc `@param`.** An options object's `= {}`
|
|
92
|
+
stays only where a real caller passes nothing and every member has its own
|
|
93
|
+
default.
|
|
94
|
+
|
|
95
|
+
Why: the same one-step reading as server code; `useX(options)` read in the
|
|
96
|
+
body hides what it needs.
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
// good
|
|
100
|
+
/** @param {{ uploadAttachment: AttachmentUpload, onUploaded: (attachment: Attachment) => void }} options */
|
|
101
|
+
function useAttachments({ uploadAttachment, onUploaded }) {
|
|
102
|
+
|
|
103
|
+
// bad
|
|
104
|
+
function useAttachments(options = {}) {
|
|
105
|
+
const upload = options.uploadAttachment;
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Required options
|
|
109
|
+
|
|
110
|
+
**A collaborator every real caller passes is required: no `= null`, `= {}` or
|
|
111
|
+
no-op default, no `typeof x === "function"` probe and no `x?.()` call.** A
|
|
112
|
+
default that is the production implementation stays (`now = Date.now`).
|
|
113
|
+
|
|
114
|
+
Why: a probe gives each call a second outcome that production never takes.
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
// good
|
|
118
|
+
function useAttachments({ uploadAttachment }) {
|
|
119
|
+
|
|
120
|
+
// bad: every caller passes uploadAttachment, yet it is probed
|
|
121
|
+
function useAttachments({ uploadAttachment = null } = {}) {
|
|
122
|
+
if (typeof uploadAttachment !== "function") return;
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Absent options
|
|
126
|
+
|
|
127
|
+
**What a real caller leaves out takes one of the server's two forms.** A
|
|
128
|
+
per-call callback (`onError`, `onProgress`) defaults to a named no-op and is
|
|
129
|
+
called plainly. An optional operation, a contract member some real caller
|
|
130
|
+
omits, is typed `?`, received with no default, probed once with an
|
|
131
|
+
`optional operation:` allow comment, and branched on that one boolean.
|
|
132
|
+
|
|
133
|
+
- When the member is read live (an application binding behind a getter), probe
|
|
134
|
+
it where it is called, with one allow comment, through one local reader
|
|
135
|
+
function when several sites call it.
|
|
136
|
+
- In a template, call it in the binding, `adapter.actions.reload?.()`, with one
|
|
137
|
+
HTML allow comment directly above the element's opening tag; it covers that
|
|
138
|
+
element's bindings, not its children's. Never `a?.b && a.b()`.
|
|
139
|
+
- A maybe-ref input (`MaybeRefOrGetter<T>`) is read with Vue's `toValue()`.
|
|
140
|
+
- A function whose one job is normalizing a documented external value of
|
|
141
|
+
several shapes carries one `input validation:` allow comment directly above
|
|
142
|
+
it, which covers the probes in its own body. Nowhere else: never over a
|
|
143
|
+
composable, a `setup()` or a function that does more than normalize.
|
|
144
|
+
|
|
145
|
+
A test-only omission counts for none of these: complete the double.
|
|
146
|
+
|
|
147
|
+
Why: one spelling for "may be absent" across server and client, and one probe
|
|
148
|
+
instead of one per call.
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
// good
|
|
152
|
+
function ignoreProgress() {}
|
|
153
|
+
function useUpload({ onProgress = ignoreProgress }) {
|
|
154
|
+
onProgress(0);
|
|
155
|
+
}
|
|
156
|
+
/** @param {{ deleteAttachment?: AttachmentDelete }} options */
|
|
157
|
+
function useAttachments({ deleteAttachment }) {
|
|
158
|
+
// readable-check: allow method-probe - optional operation: a read-only queue has no deleteAttachment, so removal stays local
|
|
159
|
+
const canDelete = typeof deleteAttachment === "function";
|
|
160
|
+
}
|
|
161
|
+
const text = toValue(source);
|
|
162
|
+
|
|
163
|
+
// bad
|
|
164
|
+
function useUpload({ onProgress = null }) {
|
|
165
|
+
onProgress?.(0);
|
|
166
|
+
}
|
|
167
|
+
const text = typeof source === "function" ? source() : source.value;
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```html
|
|
171
|
+
<!-- good -->
|
|
172
|
+
<!-- readable-check: allow optional-call - optional operation: a read-only transcript has no reload -->
|
|
173
|
+
<v-btn :disabled="loading" @click="adapter.actions.reload?.()" />
|
|
174
|
+
|
|
175
|
+
<!-- bad -->
|
|
176
|
+
<v-btn v-if="adapter.actions?.reload" @click="adapter.actions.reload()" />
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Platform probes
|
|
180
|
+
|
|
181
|
+
**A probe of a host object lives in one helper per package,
|
|
182
|
+
`hasWindowFunction(name)`, with one `platform object:` allow comment, and is
|
|
183
|
+
imported.**
|
|
184
|
+
|
|
185
|
+
Why: one treatment per probe kind; copies of a `window` probe drift.
|
|
186
|
+
|
|
187
|
+
```js
|
|
188
|
+
// good: src/client/browser.js
|
|
189
|
+
/** Whether the page's window has the named function; outside a browser there is no window. */
|
|
190
|
+
function hasWindowFunction(name) {
|
|
191
|
+
// readable-check: allow method-probe - platform object: window and its functions exist only in a browser
|
|
192
|
+
return typeof window !== "undefined" && typeof window[name] === "function";
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// bad, in five places
|
|
196
|
+
if (typeof window !== "undefined" && typeof window.clearTimeout === "function") window.clearTimeout(timer);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Client providers
|
|
200
|
+
|
|
201
|
+
**A client provider follows the server's token rules: `requires`, `optional`
|
|
202
|
+
and `provides` one per line, hooks typed with the kernel's or the owner's
|
|
203
|
+
types, registration in `setup()`, a replaceable value as an `optional` token
|
|
204
|
+
with a named default, and no module-level registry.**
|
|
205
|
+
|
|
206
|
+
Why: client providers are capabilities like server features; a slot filled by
|
|
207
|
+
a setter or a module map has no visible owner.
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
// good
|
|
211
|
+
const ConversationClientProvider = defineProvider({
|
|
212
|
+
id: "conversation.web.client",
|
|
213
|
+
requires: {
|
|
214
|
+
components: "client.components"
|
|
215
|
+
},
|
|
216
|
+
/** @param {{ components: import("@jskit-ai/kernel/client/types").ClientComponents }} capabilities */
|
|
217
|
+
setup({ components }) {
|
|
218
|
+
components.register("conversation.web.settings.element", ConversationSettingsElement);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// bad
|
|
223
|
+
const components = new Map();
|
|
224
|
+
export function registerComponent(id, component) { components.set(id, component); }
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Injected values
|
|
228
|
+
|
|
229
|
+
**`inject()` takes the owner's exported key, never a literal. A required value
|
|
230
|
+
is `inject(KEY)`; one that may be absent is `inject(KEY, NAMED_DEFAULT)` with
|
|
231
|
+
the named default exported beside the key. The binding names its type.** A
|
|
232
|
+
key another package owns is imported, with its type and disabled default, from
|
|
233
|
+
that package; a consumer that cannot depend on it writes the key in place,
|
|
234
|
+
typed, with a named `NO_*` default.
|
|
235
|
+
|
|
236
|
+
Why: injection is the client's capability lookup: one key, one type, one
|
|
237
|
+
resolution.
|
|
238
|
+
|
|
239
|
+
```js
|
|
240
|
+
// good
|
|
241
|
+
/** @type {import("@jskit-ai/workspaces-web/client/types").WorkspaceScopeSupport} */
|
|
242
|
+
const support = inject(WORKSPACE_SCOPE_KEY, NO_WORKSPACE_SCOPE);
|
|
243
|
+
|
|
244
|
+
// bad
|
|
245
|
+
const support = inject("workspaceScope", null);
|
|
246
|
+
if (!support || typeof support.readRouteScope !== "function") return FALLBACK;
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Components
|
|
250
|
+
|
|
251
|
+
### Collaborator props
|
|
252
|
+
|
|
253
|
+
**Every prop has a `type` and is required or has a real default. A prop the
|
|
254
|
+
component calls is `required: true`, typed with a `PropType` cast to its
|
|
255
|
+
typedef, and called without `?.`.** A collaborator prop that real callers
|
|
256
|
+
genuinely leave out keeps `default: null`, is typed `PropType<X | null>`,
|
|
257
|
+
carries one `optional operation:` allow comment and is branched on once.
|
|
258
|
+
|
|
259
|
+
Why: a `default: null` collaborator is a probe at every use; a chain of `?.`
|
|
260
|
+
calls on one adapter hides which parts are really optional.
|
|
261
|
+
|
|
262
|
+
```js
|
|
263
|
+
// good
|
|
264
|
+
const props = defineProps({
|
|
265
|
+
state: { type: /** @type {import("vue").PropType<GoalState>} */ (Object), required: true }
|
|
266
|
+
});
|
|
267
|
+
const canSet = computed(() => !props.state.loading && props.state.available);
|
|
268
|
+
|
|
269
|
+
// bad
|
|
270
|
+
const props = defineProps({ state: { type: Object, default: null } });
|
|
271
|
+
const canSet = computed(() => typeof props.state?.set === "function");
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Declared emits
|
|
275
|
+
|
|
276
|
+
**`defineEmits()` lists every event the component emits, including those a
|
|
277
|
+
composable emits through the `emit` it receives.**
|
|
278
|
+
|
|
279
|
+
Why: the declaration is the component's event contract, and an undeclared
|
|
280
|
+
event also falls through as a DOM listener.
|
|
281
|
+
|
|
282
|
+
```js
|
|
283
|
+
// good
|
|
284
|
+
const emit = defineEmits(["close", "closed"]);
|
|
285
|
+
|
|
286
|
+
// bad
|
|
287
|
+
const emit = defineEmits(["close"]);
|
|
288
|
+
emit("closed");
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Presenting scripts
|
|
292
|
+
|
|
293
|
+
**A component's `<script>` presents: props, emits, element refs, display
|
|
294
|
+
`computed`s and handlers that call a composable or emit.** Requests, state
|
|
295
|
+
machines, timers, parsing and projections live in a composable or a plain
|
|
296
|
+
module, moved verbatim. Split by concern into a few cohesive pieces; a large
|
|
297
|
+
style block moves to `<style scoped src="./X.css">`. Never a subcomponent made
|
|
298
|
+
only to shrink a file: it changes scoped styles and slots.
|
|
299
|
+
|
|
300
|
+
Why: logic in a composable is read and tested without mounting a component.
|
|
301
|
+
|
|
302
|
+
```text
|
|
303
|
+
good: Transcript.vue (template and presentation), useTranscriptScroll.js,
|
|
304
|
+
transcriptEntries.js (pure projection), Transcript.css via <style scoped src>
|
|
305
|
+
bad: Transcript.vue, 1,600 lines: rendering, scroll anchoring, delivery state and 500 lines of CSS
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Composables
|
|
309
|
+
|
|
310
|
+
### Fixed result shape
|
|
311
|
+
|
|
312
|
+
**A composable returns one object literal of named members, the same on every
|
|
313
|
+
path: nothing assigned after construction, no spread of another composable's
|
|
314
|
+
result, no conditional keys, and never `Object.freeze`.** That includes a
|
|
315
|
+
result a callback builds: a wrapper passes what the result needs into the
|
|
316
|
+
callback instead of patching what it returned. An extension that needs the
|
|
317
|
+
object it extends is listed in the literal (`application: null`) and filled
|
|
318
|
+
once right after construction, with a `cycle:` allow comment. A plain
|
|
319
|
+
`create*` that holds no Vue state keeps `Object.freeze`.
|
|
320
|
+
|
|
321
|
+
Why: a fixed shape keeps the type true; and `reactive()` leaves a frozen
|
|
322
|
+
object unproxied while `proxyRefs()` (applied to a `setup()` return and to
|
|
323
|
+
`defineExpose()`) throws on one, so Vue state is never frozen.
|
|
324
|
+
|
|
325
|
+
```js
|
|
326
|
+
// good
|
|
327
|
+
const suggestions = useSuggestions();
|
|
328
|
+
return { messages, send, suggestions };
|
|
329
|
+
const runtime = app.runWithContext(() => createRuntime(readers, retain));
|
|
330
|
+
|
|
331
|
+
// bad
|
|
332
|
+
return Object.freeze({ messages, send, ...useSuggestions() });
|
|
333
|
+
const runtime = app.runWithContext(() => createRuntime(readers));
|
|
334
|
+
runtime.retain = retain;
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Effect order
|
|
338
|
+
|
|
339
|
+
**Splitting a composable keeps its `watch()` and `watchEffect()` calls in the
|
|
340
|
+
original composable, in their creation order, calling the extracted module's
|
|
341
|
+
functions.**
|
|
342
|
+
|
|
343
|
+
Why: pre-flush watchers in one scope run in trigger order, so moving them into
|
|
344
|
+
a helper composable can reorder an application's callbacks.
|
|
345
|
+
|
|
346
|
+
```js
|
|
347
|
+
// good: useConversation.js keeps its watchers in order
|
|
348
|
+
watch(messages, (next) => scroll.follow(next));
|
|
349
|
+
watch(draft, (text) => drafts.save(text));
|
|
350
|
+
|
|
351
|
+
// bad: one watcher moved into useDrafts(), now created before the scroll watcher
|
|
352
|
+
const drafts = useDrafts(draft);
|
|
353
|
+
watch(messages, (next) => scroll.follow(next));
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Released listeners
|
|
357
|
+
|
|
358
|
+
**Everything client code starts, it stops: each `addEventListener`,
|
|
359
|
+
`setInterval`, observer and `.on()` is released in `onScopeDispose` or
|
|
360
|
+
`onBeforeUnmount`, or registered with `{ once: true }` or an `AbortSignal`
|
|
361
|
+
aborted there.** A listener on an object the code creates with `new` and drops
|
|
362
|
+
with it (its own socket, closed in `disconnect`) goes with that object.
|
|
363
|
+
|
|
364
|
+
Why: a listener that outlives its component leaks memory and fires against
|
|
365
|
+
unmounted state.
|
|
366
|
+
|
|
367
|
+
```js
|
|
368
|
+
// good
|
|
369
|
+
onMounted(() => window.addEventListener("resize", measure));
|
|
370
|
+
onBeforeUnmount(() => window.removeEventListener("resize", measure));
|
|
371
|
+
|
|
372
|
+
// bad
|
|
373
|
+
onMounted(() => window.addEventListener("resize", () => measure()));
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Module state
|
|
377
|
+
|
|
378
|
+
**State lives inside the composable or factory that owns it. A module-level
|
|
379
|
+
`let`, `ref()` or `reactive()` exists only when every instance on the page
|
|
380
|
+
must share it, with a `shared resource:` allow comment.** A private memo
|
|
381
|
+
cache nothing fills from outside stays.
|
|
382
|
+
|
|
383
|
+
Why: module scope is shared by every component instance and every application
|
|
384
|
+
on the page, and survives unmounting.
|
|
385
|
+
|
|
386
|
+
```js
|
|
387
|
+
// good: each composer owns its draft
|
|
388
|
+
function useComposer() {
|
|
389
|
+
const draft = ref("");
|
|
390
|
+
}
|
|
391
|
+
// readable-check: allow module-state - shared resource: every attachment queue on the page draws ids from one sequence
|
|
392
|
+
let attachmentClientSequence = 0;
|
|
393
|
+
|
|
394
|
+
// bad: every composer on the page shares one draft
|
|
395
|
+
let pendingDraft = "";
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## Helpers and sizes
|
|
399
|
+
|
|
400
|
+
### One helper per package
|
|
401
|
+
|
|
402
|
+
**A pure helper exists once in a package and is imported; one the kernel owns
|
|
403
|
+
(`isRecord`, `normalizeText`) comes from
|
|
404
|
+
`@jskit-ai/kernel/shared/support/normalize`; one client and server code both
|
|
405
|
+
need lives in the package's `src/shared`.**
|
|
406
|
+
|
|
407
|
+
Why: copies drift, and the next reader cannot tell which is right.
|
|
408
|
+
|
|
409
|
+
```js
|
|
410
|
+
// good
|
|
411
|
+
import { isRecord } from "@jskit-ai/kernel/shared/support/normalize";
|
|
412
|
+
|
|
413
|
+
// bad: a copy in src/client/lib/api.js
|
|
414
|
+
function isRecord(value) { return Boolean(value) && typeof value === "object" && !Array.isArray(value); }
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Size limits
|
|
418
|
+
|
|
419
|
+
**Files stay under 750 lines and functions under 600; a `.vue` file counts
|
|
420
|
+
whole: template, script and style.** Split by concern
|
|
421
|
+
([presenting scripts](#presenting-scripts)), never into tiny modules
|
|
422
|
+
([simple to reason about](#simple-to-reason-about)).
|
|
423
|
+
|
|
424
|
+
Why: a reader holds one concern at a time.
|
|
425
|
+
|
|
426
|
+
```text
|
|
427
|
+
good: Transcript.vue 420 lines, useTranscriptScroll.js 180, transcriptEntries.js 240
|
|
428
|
+
bad: Transcript.vue 1,600 lines; or eight 90-line files named transcriptPart1.js to transcriptPart8.js
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## Checks
|
|
432
|
+
|
|
433
|
+
### Running the checks
|
|
434
|
+
|
|
435
|
+
`clientFeaturesConfig(globs)` makes the client rules errors for the client
|
|
436
|
+
code the globs select, Vue components included and tests excluded. A new
|
|
437
|
+
application lists all of it; an existing one lists each client tree as it
|
|
438
|
+
converts it, and nothing else changes until it does:
|
|
439
|
+
|
|
440
|
+
```js
|
|
441
|
+
import { baseConfig, clientFeaturesConfig, typedFeaturesConfig, vueConfig } from "@jskit-ai/config-eslint/server";
|
|
442
|
+
|
|
443
|
+
export default [
|
|
444
|
+
...baseConfig,
|
|
445
|
+
...typedFeaturesConfig(["packages/*/src/server/**", "packages/*/src/shared/**"]),
|
|
446
|
+
...vueConfig,
|
|
447
|
+
...clientFeaturesConfig(["packages/*/src/client/**", "src/**"])
|
|
448
|
+
];
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
It runs the typed-features rules that read client code, with `use*`
|
|
452
|
+
composables counted as factories and templates read for probes, and the
|
|
453
|
+
client-only rules below. The server's action and error rules do not apply.
|
|
454
|
+
|
|
455
|
+
### Lint rules
|
|
456
|
+
|
|
457
|
+
| Rule | Reports | Section |
|
|
458
|
+
| --- | --- | --- |
|
|
459
|
+
| `jskit/typed-inject` | `inject()` with a literal key, an untyped binding, or a `null` / `{}` default | [Injected values](#injected-values) |
|
|
460
|
+
| `jskit/collaborator-props` | a prop the component calls that is optional with no named default, or typed only `Object` | [Collaborator props](#collaborator-props) |
|
|
461
|
+
| `jskit/fixed-result-shape` | behaviour assigned to a `use*` / `create*` result after it is built, also one a wrapper's callback built | [Fixed result shape](#fixed-result-shape) |
|
|
462
|
+
| `jskit/no-frozen-vue-state` | `Object.freeze` over refs, computeds or reactive state | [Fixed result shape](#fixed-result-shape) |
|
|
463
|
+
| `jskit/released-listeners` | a listener, interval, observer or subscription the file starts and never releases | [Released listeners](#released-listeners) |
|
|
464
|
+
| `jskit/no-module-state` | a module-level `let`, `ref()` or `reactive()` | [Module state](#module-state) |
|
|
465
|
+
| `vue/require-prop-types`, `vue/require-default-prop`, `vue/require-valid-default-prop`, `vue/no-required-prop-with-default` | a prop without a type, optional without a default, or both | [Collaborator props](#collaborator-props) |
|
|
466
|
+
| `vue/require-explicit-emits` | an emitted event the component does not declare | [Declared emits](#declared-emits) |
|
|
467
|
+
|
|
468
|
+
The typed-features rules keep their own sections:
|
|
469
|
+
[`typed-dependencies`](typed-features.md#typed-collaborators),
|
|
470
|
+
[`required-collaborators`](typed-features.md#required-collaborators),
|
|
471
|
+
[`no-duplicate-helper`](typed-features.md#one-helper-per-package),
|
|
472
|
+
[`no-factory-spread`](typed-features.md#named-members) and the
|
|
473
|
+
rest. In client code, `no-slot-setter` reports only a setter named like a slot
|
|
474
|
+
(`setProvider`), `frozen-factory-result` leaves Vue state unfrozen, and
|
|
475
|
+
`no-duplicate-helper` compares a client helper with the whole package, server
|
|
476
|
+
and shared code included.
|
|
477
|
+
|
|
478
|
+
Simplicity, effect order and presenting scripts are checked by reading.
|
|
479
|
+
|
|
480
|
+
### Exceptions
|
|
481
|
+
|
|
482
|
+
Client exceptions use the
|
|
483
|
+
[typed features exceptions](typed-features.md#exceptions): a
|
|
484
|
+
comment on the line before the finding, naming the rule or the finding, with a
|
|
485
|
+
reason prefix. Client code adds three placements:
|
|
486
|
+
|
|
487
|
+
| Placement | The exception |
|
|
488
|
+
| --- | --- |
|
|
489
|
+
| `// readable-check: allow module-state - shared resource: <what the page shares>` | module state every instance on the page shares on purpose |
|
|
490
|
+
| `<!-- readable-check: allow optional-call - optional operation: <what lacks it> -->` above an element | that element's bindings call an optional operation |
|
|
491
|
+
| `// readable-check: allow method-probe - input validation: <the shapes>` above a function | the probes of a function whose one job is normalizing an external value of several shapes |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- Generated from `packages/agent-docs/site/guide/framework/crud-operations.md
|
|
1
|
+
<!-- Generated from `packages/agent-docs/site/guide/framework/crud-operations.md`. -->
|
|
2
2
|
|
|
3
3
|
# CRUD operations
|
|
4
4
|
|
|
@@ -311,7 +311,7 @@ behavior. An explicit `client` override receives the same request options.
|
|
|
311
311
|
Server authorization is unchanged.
|
|
312
312
|
|
|
313
313
|
For custom form actions, see
|
|
314
|
-
[record form actions](
|
|
314
|
+
[record form actions](crud-form-actions.md).
|
|
315
315
|
|
|
316
316
|
## Strict temporal values
|
|
317
317
|
|
|
@@ -332,11 +332,11 @@ make a live table or a generator the sole source of truth. Inspect schemas for
|
|
|
332
332
|
adoption and diagnosis; inspection is optional.
|
|
333
333
|
|
|
334
334
|
Read [ordering and recovery](https://mobily-enterprises.github.io/jskit-ai/guide/app-setup/database-layer#directory-order-comes-before-filenames)
|
|
335
|
-
before schema work; check Knex configuration and ledger.
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
335
|
+
before schema work; check Knex configuration and ledger. Package table
|
|
336
|
+
migrations run first, then root `migrations/`, then the constraints phases.
|
|
337
|
+
Timestamps order only within directories. Root migrations may reference package
|
|
338
|
+
tables; keep tables/columns with their package and put foreign keys between
|
|
339
|
+
packages in a constraints phase.
|
|
340
340
|
|
|
341
341
|
MySQL/MariaDB can leave committed DDL without a ledger entry. Check columns and
|
|
342
342
|
keys separately; `hasColumn()` is insufficient. Validate existing definitions,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- Generated from `packages/agent-docs/site/guide/app-setup/existing-application-migration.md
|
|
1
|
+
<!-- Generated from `packages/agent-docs/site/guide/app-setup/existing-application-migration.md`. -->
|
|
2
2
|
|
|
3
3
|
# Migrate an existing application
|
|
4
4
|
|
|
@@ -61,7 +61,9 @@ Model product domains through `defineFeature()` when they own a real capability
|
|
|
61
61
|
or operation boundary. A Feature declares stable named `requires`, optional
|
|
62
62
|
inputs, and `provides`; `setup()` receives those values directly and returns
|
|
63
63
|
only the promised outputs. Named actions capture the exact feature APIs they
|
|
64
|
-
need.
|
|
64
|
+
need. Write Features and providers as [typed features](typed-features.md):
|
|
65
|
+
each collaborator is declared, typed where it is received, and never probed.
|
|
66
|
+
Register structural catalogue entries during `setup()` and reserve
|
|
65
67
|
`boot()` for long-lived work that starts after composition. `shutdown()` stops
|
|
66
68
|
that work. The runtime shuts Features down in reverse order and keeps going
|
|
67
69
|
after one fails; it then rejects with an `AggregateError` holding one error per
|
|
@@ -230,8 +232,8 @@ Invitation creation, acceptance and refusal now own their managed transactions:
|
|
|
230
232
|
- Replace `members.createInvite(..., { trx })` with
|
|
231
233
|
`members.prepareInvite(..., { trx })`, save the application association/receipt,
|
|
232
234
|
commit, then call `members.sendInvite(...)` without `trx`.
|
|
233
|
-
- Move related acceptance writes into
|
|
234
|
-
`
|
|
235
|
+
- Move related acceptance writes into a function provided as the
|
|
236
|
+
`workspaces.invite-acceptance-participant` capability.
|
|
235
237
|
Acceptance/refusal reject borrowed transactions; revocation can still join one.
|
|
236
238
|
- Terminal repository transitions reject accepted/revoked invitations. Mailer
|
|
237
239
|
exceptions return a fixed message to keep tokens out of provider diagnostics.
|
|
@@ -239,7 +241,7 @@ Invitation creation, acceptance and refusal now own their managed transactions:
|
|
|
239
241
|
Participant writes roll back together. Preserve transaction outcomes when handling
|
|
240
242
|
errors: completion-hook failures cannot undo a commit, and expiry revocation
|
|
241
243
|
commits before returning 409. See the
|
|
242
|
-
[workspace invitation contract and example](
|
|
244
|
+
[workspace invitation contract and example](multi-homing.md#transactional-invitation-participation).
|
|
243
245
|
Action IDs, response shapes and schema are unchanged.
|
|
244
246
|
|
|
245
247
|
The first coordinated release includes
|
|
@@ -257,11 +259,13 @@ historical migration when moving it so the existing Knex ledger continues to
|
|
|
257
259
|
recognize it. Basenames must be unique across the effective graph.
|
|
258
260
|
|
|
259
261
|
Keep cross-package foreign keys and other dependency-sensitive constraints in
|
|
260
|
-
|
|
261
|
-
`
|
|
262
|
-
`migrations
|
|
263
|
-
|
|
264
|
-
|
|
262
|
+
a constraints phase of `createKnexMigrationConfigFromApp()`: the referencing
|
|
263
|
+
package's own `constraints/` subdirectory, which runs after root `migrations/`,
|
|
264
|
+
or application `migrations/constraints/`, which runs last. It uses
|
|
265
|
+
`sortDirsSeparately: true`: the sorted package directories run before root
|
|
266
|
+
`migrations/`, and filenames only order work within their own directory. A later
|
|
267
|
+
timestamp in a package does not defer its foreign key until another package has
|
|
268
|
+
created the parent table.
|
|
265
269
|
Follow the [database ordering and partial-failure recovery rules](https://mobily-enterprises.github.io/jskit-ai/guide/app-setup/database-layer#directory-order-comes-before-filenames),
|
|
266
270
|
including independent checks for columns and constraints after MySQL or MariaDB
|
|
267
271
|
DDL fails. Seeds are not migrations: run one explicit application-owned,
|
|
@@ -312,17 +316,16 @@ Do not conflate these identities:
|
|
|
312
316
|
|
|
313
317
|
An application with accounts has the standard managed-preview login: retain
|
|
314
318
|
the app-owned `tools/preview-identity` executable, or add it from
|
|
315
|
-
`auth/auth-surface` when it is missing,
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
consumer-owned section without interpreting it. The executable calls the
|
|
319
|
+
`auth/auth-surface` when it is missing, pass it the preview host's protocol id
|
|
320
|
+
and hostnames, and declare it to the host as that pattern shows. JSKIT owns
|
|
321
|
+
the executable/library pattern; the host owns the declaration's grammar, its
|
|
322
|
+
protocol id and its runtime behavior. The executable calls the
|
|
320
323
|
`@jskit-ai/auth-web` server-side managed-preview library; it does not need a
|
|
321
324
|
framework CLI. Never expose the exchange secret in browser code, a URL, client
|
|
322
325
|
environment, logs, or source.
|
|
323
326
|
|
|
324
327
|
Managed Playwright uses the host-provided
|
|
325
|
-
`
|
|
328
|
+
`PLAYWRIGHT_STORAGE_STATE`, relative URLs, and the existing managed
|
|
326
329
|
server. Direct-local tests may instead use `loginAsExistingUser()` from
|
|
327
330
|
`@jskit-ai/auth-web/test/playwright` with a server-only development secret.
|
|
328
331
|
Do not mix those two authentication modes or start a duplicate server against
|
|
@@ -348,10 +351,11 @@ do not accept a visually similar screen as behavioral equivalence.
|
|
|
348
351
|
|
|
349
352
|
## 7. Reconcile, then Deslop as a distinct pass
|
|
350
353
|
|
|
351
|
-
When the project
|
|
352
|
-
functionally green. Compare the implemented
|
|
353
|
-
|
|
354
|
-
|
|
354
|
+
When the project keeps authoritative project documentation, run an explicit
|
|
355
|
+
reconciliation after the port is functionally green. Compare the implemented
|
|
356
|
+
source with its current product description, selected stack, subsystem notes,
|
|
357
|
+
resources, managed skills, and effective workspace and output recipes. Update
|
|
358
|
+
stale citations and declarations to describe what now
|
|
355
359
|
exists. Do not duplicate an inherited setup recipe merely to make the project
|
|
356
360
|
look self-contained.
|
|
357
361
|
|
|
@@ -376,10 +380,10 @@ evidence in ordinary source, configuration, command output, and tests:
|
|
|
376
380
|
1. **Behavior:** focused tests and the full managed browser suite are green
|
|
377
381
|
against an isolated disposable database. Use the managed host and server
|
|
378
382
|
when supplied.
|
|
379
|
-
2. **Reconciliation:** a dedicated
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
+
2. **Reconciliation:** a dedicated reconciliation confirms the project's
|
|
384
|
+
authoritative product and host documentation (product description, selected
|
|
385
|
+
stack, subsystem notes, resources, managed skills, and effective setup and
|
|
386
|
+
output recipes) matches the implementation.
|
|
383
387
|
3. **Deslop:** a distinct JSKIT Deslop pass, including the selected Material 3
|
|
384
388
|
audit where UI is affected, removes obsolete framework residue without
|
|
385
389
|
changing legitimate behavior.
|
|
@@ -395,9 +399,9 @@ Do not satisfy a gate by adding a completion ledger or generated conformance
|
|
|
395
399
|
state. If a command cannot run, report the missing evidence rather than marking
|
|
396
400
|
the port complete.
|
|
397
401
|
|
|
398
|
-
## Downstream proof:
|
|
402
|
+
## Downstream proof: a production port
|
|
399
403
|
|
|
400
|
-
|
|
404
|
+
A production application validated this procedure on a substantial existing product rather
|
|
401
405
|
than a sample. The port redistributed 66 historical migrations among 43 owning
|
|
402
406
|
packages while retaining globally unique basenames and putting cross-package
|
|
403
407
|
constraints last. Its imported database ledger remained read-only and
|
|
@@ -411,7 +415,7 @@ and safety behavior. Obsolete framework generators, provenance markers,
|
|
|
411
415
|
service-location wiring, and empty extension modules were removed without
|
|
412
416
|
flattening those workflows.
|
|
413
417
|
|
|
414
|
-
After a separate
|
|
418
|
+
After a separate reconciliation and JSKIT/Material Deslop, its focused
|
|
415
419
|
checks, 339 server tests, client tests, production build, database proofs, and
|
|
416
420
|
all 105 managed Playwright cases passed. These numbers are downstream evidence
|
|
417
421
|
that the method scales; they are not template targets or state that another
|