@jskit-ai/agent-docs 0.1.283 → 0.1.284
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 +14 -2
- package/guide/agent/app-extras/realtime.md +38 -1
- package/guide/agent/app-setup/authentication.md +64 -14
- package/guide/agent/app-setup/console.md +8 -0
- package/guide/agent/app-setup/database-layer.md +23 -103
- package/guide/agent/app-setup/existing-application-migration.md +105 -72
- package/guide/agent/app-setup/multi-homing.md +94 -32
- package/guide/agent/app-setup/quickstart.md +1 -1
- package/guide/agent/app-setup/run-an-accounts-hub.md +1 -1
- package/guide/agent/app-setup/upgrading-jskit.md +8 -3
- package/guide/agent/app-setup/users.md +8 -5
- package/guide/agent/framework/application-operations.md +1 -1
- package/guide/agent/framework/crud-operations.md +63 -45
- package/guide/agent/framework/data-access.md +1031 -0
- package/guide/agent/framework/sanity-checks.md +26 -27
- package/guide/agent/framework/typed-features.md +114 -247
- package/guide/agent/index.md +2 -0
- package/package.json +6 -3
- package/patterns/feature-package/PATTERN.md +25 -17
- package/patterns/feature-package/example/booking-engine/package.json +1 -1
- package/patterns/feature-package/example/variations/InvoiceRollupFeature.js +6 -7
- package/patterns/feature-package/example/variations/invoiceResource.js +15 -0
- package/patterns/feature-package/example/variations/invoiceRollupActions.js +4 -4
- package/patterns/feature-package/example/variations/ownedTableRepository.js +27 -0
- package/patterns/feature-package/example/variations/types.js +1 -1
- package/patterns/minimal-foundation/example/AGENTS.md +4 -0
- package/patterns/minimal-foundation/example/package.json +4 -4
- package/patterns/minimal-foundation/example/server.js +4 -1
- package/patterns/shell-foundation/example/AGENTS.md +4 -0
- package/patterns/shell-foundation/example/package.json +5 -5
- package/patterns/shell-foundation/example/packages/main/package.json +1 -1
- package/patterns/shell-foundation/example/server.js +4 -1
- package/reference/autogen/KERNEL_MAP.md +2 -1
- package/reference/autogen/PATTERN_INDEX.md +70 -59
- package/reference/autogen/README.md +1 -1
- package/reference/autogen/packages/agent-docs.md +7 -3
- package/reference/autogen/packages/agent-runtime.md +48 -11
- package/reference/autogen/packages/architecture-inspector.md +6 -2
- package/reference/autogen/packages/assistant-core.md +4 -4
- package/reference/autogen/packages/assistant-runtime.md +33 -13
- package/reference/autogen/packages/auth-provider-local-core.md +3 -1
- package/reference/autogen/packages/auth-provider-local-db-core.md +65 -14
- package/reference/autogen/packages/auth-provider-oidc-core.md +5 -1
- package/reference/autogen/packages/auth-provider-supabase-core.md +1 -1
- package/reference/autogen/packages/connectors-core.md +22 -2
- package/reference/autogen/packages/console-core.md +9 -5
- package/reference/autogen/packages/crud-core.md +38 -173
- package/reference/autogen/packages/database-runtime.md +8 -32
- package/reference/autogen/packages/http-runtime.md +3 -3
- package/reference/autogen/packages/http-web.md +12 -0
- package/reference/autogen/packages/kernel.md +25 -2
- package/reference/autogen/packages/oidc-issuer-core.md +4 -0
- package/reference/autogen/packages/payments-core.md +29 -6
- package/reference/autogen/packages/realtime.md +15 -6
- package/reference/autogen/packages/resource-core.md +30 -5
- package/reference/autogen/packages/resource-crud-core.md +30 -34
- package/reference/autogen/packages/resource-runtime.md +198 -0
- package/reference/autogen/packages/rewarded-core.md +16 -10
- package/reference/autogen/packages/shell-web.md +1 -0
- package/reference/autogen/packages/users-core.md +26 -14
- package/reference/autogen/packages/workspaces-core.md +17 -10
- package/reference/autogen/tooling/config-eslint.md +69 -0
- package/reference/autogen/tooling/jskit-catalog.md +36 -1
- package/reference/autogen/tooling/jskit-release.md +3 -0
- package/skills/jskit/SKILL.md +4 -1
- package/skills/jskit/references/app-operations.md +1 -1
- package/skills/jskit/references/crud-operations.md +62 -44
- package/skills/jskit/references/data-access.md +1026 -0
- package/skills/jskit/references/database-layer.md +411 -0
- package/skills/jskit/references/existing-application-migration.md +105 -72
- package/skills/jskit/references/heuristics/INDEX.md +6 -4
- package/skills/jskit/references/heuristics/crud-authoring.md +22 -14
- package/skills/jskit/references/heuristics/data-access.md +19 -0
- package/skills/jskit/references/heuristics/filters.md +6 -6
- package/skills/jskit/references/heuristics/row-policies.md +40 -16
- package/skills/jskit/references/heuristics/server-search.md +30 -42
- package/skills/jskit/references/heuristics/table-resource-fields.md +78 -0
- package/skills/jskit/references/heuristics/typed-features.md +3 -3
- package/skills/jskit/references/multi-homing.md +94 -32
- package/skills/jskit/references/pattern-index.md +70 -59
- package/skills/jskit/references/patterns/app/minimal-foundation/example/AGENTS.md +4 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +4 -4
- package/skills/jskit/references/patterns/app/minimal-foundation/example/server.js +4 -1
- package/skills/jskit/references/patterns/app/shell-foundation/example/AGENTS.md +4 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +5 -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/server.js +4 -1
- package/skills/jskit/references/patterns/assistant/assistant-surface/PATTERN.md +5 -2
- package/skills/jskit/references/patterns/auth/accounts-hub/example/package.json +3 -2
- package/skills/jskit/references/patterns/auth/auth-surface/PATTERN.md +15 -0
- 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/calendar-cli/example/package.json +4 -4
- package/skills/jskit/references/patterns/crud/crud-contract/PATTERN.md +133 -0
- package/skills/jskit/references/patterns/crud/crud-contract/example/bookResource.js +74 -0
- package/skills/jskit/references/patterns/crud/{json-api-resource-package → crud-feature-package}/PATTERN.md +69 -37
- package/skills/jskit/references/patterns/crud/{json-api-resource-package → crud-feature-package}/example/packages/books/package.json +5 -4
- package/skills/jskit/references/patterns/crud/crud-feature-package/example/packages/books/src/server/BooksFeature.js +13 -0
- package/skills/jskit/references/patterns/crud/crud-feature-package/example/packages/books/src/shared/bookResource.js +74 -0
- package/skills/jskit/references/patterns/crud/crud-feature-package/example/packages/books/src/shared/index.js +1 -0
- package/skills/jskit/references/patterns/crud/crud-screen-set/PATTERN.md +26 -8
- package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookEditPage.vue +2 -2
- package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookListPage.vue +2 -2
- package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookNewPage.vue +2 -2
- package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookViewPage.vue +3 -3
- package/skills/jskit/references/patterns/database/mysql-application/PATTERN.md +9 -4
- package/skills/jskit/references/patterns/database/mysql-application/example/package.json +2 -1
- package/skills/jskit/references/patterns/database/mysql-application/example/scripts/prepare-database.js +8 -0
- package/skills/jskit/references/patterns/database/postgres-application/PATTERN.md +8 -4
- package/skills/jskit/references/patterns/database/postgres-application/example/package.json +2 -1
- package/skills/jskit/references/patterns/database/postgres-application/example/scripts/prepare-database.js +8 -0
- package/skills/jskit/references/patterns/database/table-resource/PATTERN.md +117 -0
- package/skills/jskit/references/patterns/database/table-resource/example/orderResource.js +25 -0
- package/skills/jskit/references/patterns/realtime/realtime-application/PATTERN.md +7 -0
- package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +2 -2
- package/skills/jskit/references/patterns/rewards/google-rewarded/PATTERN.md +1 -1
- package/skills/jskit/references/patterns/server/feature-package/PATTERN.md +25 -17
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/package.json +1 -1
- package/skills/jskit/references/patterns/server/feature-package/example/variations/InvoiceRollupFeature.js +6 -7
- package/skills/jskit/references/patterns/server/feature-package/example/variations/invoiceResource.js +15 -0
- package/skills/jskit/references/patterns/server/feature-package/example/variations/invoiceRollupActions.js +4 -4
- package/skills/jskit/references/patterns/server/feature-package/example/variations/ownedTableRepository.js +27 -0
- package/skills/jskit/references/patterns/server/feature-package/example/variations/types.js +1 -1
- package/skills/jskit/references/patterns/users/user-administration-server/PATTERN.md +59 -10
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +5 -4
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/src/server/UsersFeature.js +7 -5
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/src/shared/index.js +1 -1
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/src/shared/userCrud.js +21 -0
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +11 -5
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/src/server/UsersWorkspaceFeature.js +14 -5
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/src/server/workspaceMembersOnly.js +52 -0
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/src/shared/index.js +1 -1
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/src/shared/userCrud.js +21 -0
- package/skills/jskit/references/patterns/workspaces/workspace-server/PATTERN.md +9 -9
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/PATTERN.md +16 -0
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/pages/home/workspaces/index.vue +2 -0
- package/skills/jskit/references/sanity-checks.md +27 -28
- package/skills/jskit/references/typed-features.md +114 -247
- package/patterns/feature-package/example/variations/customKnexRepository.js +0 -40
- package/reference/autogen/packages/json-rest-api-core.md +0 -66
- package/skills/jskit/references/heuristics/crud-repository-mapping.md +0 -140
- package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/src/server/BooksFeature.js +0 -13
- package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/src/shared/bookResource.js +0 -83
- package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/src/shared/index.js +0 -1
- package/skills/jskit/references/patterns/crud/resource-contract/PATTERN.md +0 -155
- package/skills/jskit/references/patterns/crud/resource-contract/example/bookResource.js +0 -87
- package/skills/jskit/references/patterns/server/feature-package/example/variations/customKnexRepository.js +0 -40
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/src/shared/userResource.js +0 -69
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/src/shared/userResource.js +0 -56
- /package/skills/jskit/references/patterns/crud/{json-api-resource-package → crud-feature-package}/example/migrations/20260815000000_books.cjs +0 -0
- /package/skills/jskit/references/patterns/crud/{json-api-resource-package → crud-feature-package}/example/packages/books/src/server/types.js +0 -0
|
@@ -110,14 +110,25 @@ The package migrations create and own these tables:
|
|
|
110
110
|
| `workspace_invites` | `workspace_id`, `email`, `role_sid`, `status`, `token_hash`, `invited_by_user_id`, `expires_at`, `accepted_at`, `revoked_at` |
|
|
111
111
|
|
|
112
112
|
Application records that belong to a workspace use a non-null `workspace_id`
|
|
113
|
-
foreign key to `workspaces.id`. Read and change these tables through
|
|
114
|
-
`workspaces.
|
|
113
|
+
foreign key to `workspaces.id`. Read and change these tables through their
|
|
114
|
+
owners' capabilities (`workspaces`, `workspaces.memberships` and
|
|
115
|
+
`workspaces.core`, below), not directly.
|
|
115
116
|
|
|
116
117
|
## Server composition
|
|
117
118
|
|
|
118
119
|
Use `workspaces/workspace-server` for role vocabulary, creation policy,
|
|
119
120
|
invitations, settings, and app-owned invitation email rendering.
|
|
120
121
|
|
|
122
|
+
Two server settings are required, and the server refuses to start without
|
|
123
|
+
them: whether new workspaces accept invitations, and how long an invitation
|
|
124
|
+
lasts.
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
// config/server.js
|
|
128
|
+
config.workspaceSettings = { defaults: { invitesEnabled: true } };
|
|
129
|
+
config.workspaceMembers = { defaults: { inviteExpiresInMs: 7 * 24 * 60 * 60 * 1000 } };
|
|
130
|
+
```
|
|
131
|
+
|
|
121
132
|
Workspace scope must reach repository queries. Parsing a workspace slug or
|
|
122
133
|
hiding an admin link is not authorization. Membership and role checks happen
|
|
123
134
|
before an operation discloses records or record existence.
|
|
@@ -125,14 +136,12 @@ before an operation discloses records or record existence.
|
|
|
125
136
|
### Workspace-scoped features and routes
|
|
126
137
|
|
|
127
138
|
For a standard CRUD resource owned by a workspace, give its table the
|
|
128
|
-
`workspace_id` column, set
|
|
129
|
-
`
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
(`@jskit-ai/json-rest-api-core/server/jsonRestApiHost`), which throws when the
|
|
135
|
-
context names two workspaces.
|
|
139
|
+
`workspace_id` column, set its table resource's `ownership: "workspace"`, and bind
|
|
140
|
+
`defineCrudFeature()` with `scope: WORKSPACE_CRUD_SCOPE` (from
|
|
141
|
+
`@jskit-ai/workspaces-core/server/support/workspaceCrud`) on a workspace
|
|
142
|
+
surface. JSKIT then filters `workspaceId` by the workspace in the route. A
|
|
143
|
+
repository method you add passes the call's `options` on to the table's handle,
|
|
144
|
+
which scopes every read and write to the context's workspace.
|
|
136
145
|
`workspace_user` also filters by the signed-in user. See the CRUD operations
|
|
137
146
|
guide.
|
|
138
147
|
|
|
@@ -175,9 +184,29 @@ capability provides:
|
|
|
175
184
|
| `workspaces.services.members` | Members, roles and invitations |
|
|
176
185
|
| `workspaces.services.pendingInvitations` | Invitation resolution, redemption and acceptance participants |
|
|
177
186
|
| `workspaces.services.settings` | Workspace settings |
|
|
178
|
-
| `workspaces.repositories.
|
|
187
|
+
| `workspaces.repositories.workspaceSettings`, `.workspaceInvites` | Settings and invitation records inside the application's own transactions |
|
|
179
188
|
| `workspaces.tenancyProfile` | The resolved tenancy mode and workspace policy |
|
|
180
189
|
|
|
190
|
+
The workspaces and their memberships are their own capabilities, for code that
|
|
191
|
+
reads or changes them inside its own transactions, typed `Workspaces` and
|
|
192
|
+
`WorkspacesMemberships` in `@jskit-ai/workspaces-core/server/types`. Every
|
|
193
|
+
method takes the call's `options` last and joins the transaction of the
|
|
194
|
+
`txOptions` it is given.
|
|
195
|
+
|
|
196
|
+
| Capability | Methods |
|
|
197
|
+
| --- | --- |
|
|
198
|
+
| `workspaces` | `findById(id, options)`, `findBySlug(slug, options)` (a deleted workspace is not found, though its slug stays taken), `listSlugsIn`, `findPersonalByOwnerUserId`, `listForUserId(userId, options)`, `insert`, `updateById`, `lock(workspaceId, options)` |
|
|
199
|
+
| `workspaces.memberships` | `findByWorkspaceIdAndUserId`, `ensureOwnerMembership`, `upsertMembership(workspaceId, userId, patch, options)`, `addMember({ workspaceId, userId, roleSid }, options)`, `activeMemberIds(workspaceId, options)`, `listActiveByWorkspaceId`, `findActiveByWorkspaceIdAndUserId`, `listActiveWorkspaceIdsByUserId`, `listActiveWorkspaceIdsByUserIds` |
|
|
200
|
+
|
|
201
|
+
`addMember` makes the user an active member with the role (`member` when none
|
|
202
|
+
is given), adding the membership or reactivating it. `activeMemberIds` answers
|
|
203
|
+
the active members' user ids at the same cost however many members there
|
|
204
|
+
are, without including their profiles. `lock` takes
|
|
205
|
+
the workspace's row lock until the caller's transaction ends, so work that must
|
|
206
|
+
not interleave for one workspace takes turns; it runs only inside a
|
|
207
|
+
transaction, given its `txOptions`, and answers `{ id }`, or `null` when there
|
|
208
|
+
is no such workspace.
|
|
209
|
+
|
|
181
210
|
To check one person's membership, for example a notification recipient, call
|
|
182
211
|
`workspaces.services.members.getMember(workspace, userId, options)`. It answers
|
|
183
212
|
that member's `listMembers` entry, or `null` for a removed or unknown member,
|
|
@@ -225,8 +254,8 @@ workspace routes, `workspaces-web` switches Vuetify to the `workspace-light` or
|
|
|
225
254
|
colours. Other routes use the application's themes.
|
|
226
255
|
|
|
227
256
|
New workspaces start from JSKIT's blue palette unless `config/server.js` sets
|
|
228
|
-
the application's own
|
|
229
|
-
stops the server at startup:
|
|
257
|
+
the application's own beside the required `invitesEnabled`. Missing colours
|
|
258
|
+
keep JSKIT's values; an invalid colour stops the server at startup:
|
|
230
259
|
|
|
231
260
|
```js
|
|
232
261
|
// config/server.js
|
|
@@ -301,11 +330,11 @@ authorization and role checks. Do not pass arbitrary HTTP input as options.
|
|
|
301
330
|
|
|
302
331
|
| Capability method | Contract |
|
|
303
332
|
| --- | --- |
|
|
304
|
-
| `workspaces.repositories.workspaceInvites.withTransaction(
|
|
305
|
-
| `workspaces.
|
|
306
|
-
| `workspaces.services.members.prepareInvite(workspace, actor, payload,
|
|
307
|
-
| `workspaces.services.members.sendInvite(workspace, actor, prepared, options)` |
|
|
308
|
-
| `workspaces.services.members.createInvite(workspace, actor, payload, options)` | Owns preparation and commit, then calls `sendInvite`, and answers with `sendInvite`'s result as JSON:API data (`returnJsonApiData`, read it from `.value`), as the invite action sends it.
|
|
333
|
+
| `workspaces.repositories.workspaceInvites.withTransaction(options, (txOptions) => …)` | The invitations handle's `withTransaction`: joins the caller's transaction or opens one and hands `work` the `txOptions` to pass on; awaits commit and completion hooks before resolving. Runs `work` again when the database aborts it as a deadlock victim (see [data access](https://mobily-enterprises.github.io/jskit-ai/guide/framework/data-access#transactions)). `{ ...options, outermost: true }` refuses a call that already runs inside one. |
|
|
334
|
+
| `workspaces.lock(workspaceId, txOptions)` (capability `workspaces`) | Locks the workspace before application receipt/draft rows when composing preparation. Requires an active transaction. |
|
|
335
|
+
| `workspaces.services.members.prepareInvite(workspace, actor, payload, txOptions)` | Runs only inside a transaction, given its `txOptions`. Validates the invitation role, replaces pending invitations for that workspace/email, and returns `{ createdInviteId, inviteTokenPreview }`. Never sends. |
|
|
336
|
+
| `workspaces.services.members.sendInvite(workspace, actor, prepared, options)` | Refuses to run inside a transaction: it delivers after the preparation's transaction commits. Reloads the committed pending invitation and workspace display data, validates expiry and token correspondence, and sends that invitation without replacement. |
|
|
337
|
+
| `workspaces.services.members.createInvite(workspace, actor, payload, options)` | Owns preparation and commit, then calls `sendInvite`, and answers with `sendInvite`'s result as JSON:API data (`returnJsonApiData`, read it from `.value`), as the invite action sends it. Refuses to run inside a transaction. |
|
|
309
338
|
| `workspaces.invite-acceptance-participant` (optional capability) | One application function receiving `({ invite, user }, options)` inside the acceptance transaction. Without it, acceptance activates the membership only. |
|
|
310
339
|
|
|
311
340
|
`sendInvite` accepts `{ createdInviteId, inviteTokenPreview }` from preparation or
|
|
@@ -344,21 +373,20 @@ setup: ({ reviewedPeople, people, scopes, audit }) => ({
|
|
|
344
373
|
})
|
|
345
374
|
|
|
346
375
|
// Inside an authorized application command, in a feature that requires
|
|
347
|
-
// workspaces: "workspaces.core":
|
|
376
|
+
// workspaces: "workspaces.core" and workspaceRecords: "workspaces":
|
|
348
377
|
const { members } = workspaces.services;
|
|
349
378
|
const invites = workspaces.repositories.workspaceInvites;
|
|
350
|
-
const prepared = await invites.withTransaction(async (
|
|
351
|
-
const options = { trx, context };
|
|
379
|
+
const prepared = await invites.withTransaction({ context }, async (txOptions) => {
|
|
352
380
|
// Match acceptance's order: workspace, then application draft/receipt rows.
|
|
353
|
-
await
|
|
354
|
-
const reviewed = await reviewedPeople.lockAuthorizedDraft(draftId, workspace.id, actor,
|
|
355
|
-
const receipt = await reviewedPeople.findMatchingReceipt(reviewed, requestId,
|
|
381
|
+
await workspaceRecords.lock(workspace.id, txOptions);
|
|
382
|
+
const reviewed = await reviewedPeople.lockAuthorizedDraft(draftId, workspace.id, actor, txOptions);
|
|
383
|
+
const receipt = await reviewedPeople.findMatchingReceipt(reviewed, requestId, txOptions);
|
|
356
384
|
if (receipt) return { createdInviteId: receipt.inviteId };
|
|
357
385
|
const created = await members.prepareInvite(workspace, actor, {
|
|
358
386
|
email: reviewed.email,
|
|
359
387
|
roleSid: reviewed.roleSid
|
|
360
|
-
},
|
|
361
|
-
await reviewedPeople.associateInvitationAndSaveReceipt(reviewed, requestId, created.createdInviteId,
|
|
388
|
+
}, txOptions);
|
|
389
|
+
await reviewedPeople.associateInvitationAndSaveReceipt(reviewed, requestId, created.createdInviteId, txOptions);
|
|
362
390
|
return { createdInviteId: created.createdInviteId };
|
|
363
391
|
});
|
|
364
392
|
|
|
@@ -401,8 +429,8 @@ exactly once. Revocation after sending starts cannot recall a message.
|
|
|
401
429
|
Register one callback during feature setup to call the required application
|
|
402
430
|
services. Non-functions throw `TypeError`; duplicate registration throws, and
|
|
403
431
|
registration closes when acceptance/refusal first begins. The callback's invite,
|
|
404
|
-
user and options are shallow frozen. JSKIT owns the transaction and passes
|
|
405
|
-
`
|
|
432
|
+
user and options are shallow frozen. JSKIT owns the transaction and passes the
|
|
433
|
+
callback the `txOptions` that carry it and the request's `context`.
|
|
406
434
|
|
|
407
435
|
Acceptance checks authentication, recipient email, pending state and expiry under
|
|
408
436
|
workspace/invitation locks. Before calling the participant, it activates membership
|
|
@@ -420,8 +448,8 @@ callback must not commit, roll back, create transactions/savepoints or perform
|
|
|
420
448
|
external effects. Participant and later invitation-status failures roll back
|
|
421
449
|
membership (including its previous role/status), person/training links, scopes,
|
|
422
450
|
audit and accepted status. Invalid invitations and refusals do not call the
|
|
423
|
-
participant. Acceptance
|
|
424
|
-
|
|
451
|
+
participant. Acceptance and refusal refuse to run inside the caller's
|
|
452
|
+
transaction; revocation can join one or open its own.
|
|
425
453
|
|
|
426
454
|
Acceptance/refusal return 401 for unauthenticated access, 403 for a recipient
|
|
427
455
|
email mismatch and 404 for a missing or handled invitation. A matching recipient's
|
|
@@ -435,7 +463,7 @@ Preserve `error.transactionOutcome` and `error.cause`: `rolledBack` confirms
|
|
|
435
463
|
rollback, `committed` can accompany a failed completion hook, and `unknown`
|
|
436
464
|
requires resolving the outcome before retrying. Errors after commit cannot undo
|
|
437
465
|
persistence. See
|
|
438
|
-
[managed transaction guidance](./existing-application-migration.md#json-rest-
|
|
466
|
+
[managed transaction guidance](./existing-application-migration.md#data-access-on-table-resources-json-rest-api-3).
|
|
439
467
|
|
|
440
468
|
Invitation mutations lock workspace before invitation rows. A losing acceptance,
|
|
441
469
|
refusal, revocation or replacement cannot overwrite accepted/revoked state.
|
|
@@ -494,7 +522,41 @@ Workspace surfaces put the slug in their pages root, such as
|
|
|
494
522
|
`w/[workspaceSlug]/admin` for administration. Keep the default surface
|
|
495
523
|
(`surfaceDefaultId`, usually `home`) free of a workspace requirement: `/`,
|
|
496
524
|
invitation links and other unscoped pages belong to it. With
|
|
497
|
-
`webRootAllowed: "no"`, `/` opens that surface's root.
|
|
525
|
+
`webRootAllowed: "no"`, `/` opens that surface's root. The foundation
|
|
526
|
+
defines `home` and its `public` access policy, and the `auth/auth-surface`
|
|
527
|
+
pattern the `auth` surface; the workspace surfaces add a policy and two
|
|
528
|
+
definitions:
|
|
529
|
+
|
|
530
|
+
```js
|
|
531
|
+
// config/surfaceAccessPolicies.js
|
|
532
|
+
surfaceAccessPolicies.workspace_member = { requireAuth: true, requireWorkspaceMembership: true };
|
|
533
|
+
|
|
534
|
+
// config/public.js
|
|
535
|
+
config.surfaceDefinitions.app = {
|
|
536
|
+
id: "app",
|
|
537
|
+
label: "App",
|
|
538
|
+
pagesRoot: "w/[workspaceSlug]",
|
|
539
|
+
requiresAuth: true,
|
|
540
|
+
requiresWorkspace: true,
|
|
541
|
+
accessPolicyId: "workspace_member"
|
|
542
|
+
};
|
|
543
|
+
config.surfaceDefinitions.admin = {
|
|
544
|
+
id: "admin",
|
|
545
|
+
label: "Admin",
|
|
546
|
+
pagesRoot: "w/[workspaceSlug]/admin",
|
|
547
|
+
requiresAuth: true,
|
|
548
|
+
requiresWorkspace: true,
|
|
549
|
+
accessPolicyId: "workspace_member"
|
|
550
|
+
};
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
An access policy takes `requireAuth`, `requireWorkspaceMembership`,
|
|
554
|
+
`requireAnyPermissions` and `requireAllPermissions` (permission lists), and
|
|
555
|
+
`requireFlagsAny` and `requireFlagsAll` (flags the server's bootstrap sets in
|
|
556
|
+
`surfaceAccess`). Without `requireAuth` or `requireWorkspaceMembership` it
|
|
557
|
+
takes the surface's `requiresAuth` or `requiresWorkspace`. The browser applies
|
|
558
|
+
the whole policy; the server reads only `requireWorkspaceMembership: false`
|
|
559
|
+
from it, and a route's own `auth` and `permission` authorize it there.
|
|
498
560
|
|
|
499
561
|
`workspaces-web` remembers the last workspace each person opened in this
|
|
500
562
|
browser. The **Your workspaces** page, `WorkspacesClientElement` from
|
|
@@ -58,7 +58,7 @@ questions and must not be copied into committed source.
|
|
|
58
58
|
## Build the first product operation
|
|
59
59
|
|
|
60
60
|
Use the narrow relevant pattern and public APIs. For database CRUD, author an
|
|
61
|
-
immutable migration and
|
|
61
|
+
immutable migration, its table resource and, for screens, a CRUD contract; use framework APIs
|
|
62
62
|
for standard mechanics and app code for product-specific behavior. Do not
|
|
63
63
|
invoke a source generator or reconstruct one as prompt options.
|
|
64
64
|
|
|
@@ -9,7 +9,7 @@ hub shows its sign-in page, the app receives an ID token, and the app keeps its
|
|
|
9
9
|
own session.
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npm install @jskit-ai/oidc-issuer-core @jskit-ai/database-runtime-mysql
|
|
12
|
+
npm install @jskit-ai/oidc-issuer-core @jskit-ai/database-runtime @jskit-ai/database-runtime-mysql
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Start from the "JSKIT · Accounts hub" template (seed-jskit branch
|
|
@@ -62,7 +62,10 @@ npm run jskit:update
|
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
The updater rejects unknown JSKIT packages and private JSKIT overrides during
|
|
65
|
-
preflight, before changing any manifest.
|
|
65
|
+
preflight, before changing any manifest. It removes a package a release retired
|
|
66
|
+
(`@jskit-ai/json-rest-api-core`, replaced by `@jskit-ai/resource-runtime`) and
|
|
67
|
+
says what replaced it; while the application's code still imports a retired
|
|
68
|
+
package, it refuses and names the import to use instead.
|
|
66
69
|
|
|
67
70
|
`jskit:update` reads the latest published catalog, discovers the root manifest
|
|
68
71
|
and npm workspaces, and preflights every declared `@jskit-ai/*` package. It then
|
|
@@ -210,8 +213,10 @@ application:
|
|
|
210
213
|
`defineConfig(browserTestConfig({ testDir: "./tests/e2e", prepare: "tests/e2e/databases.js" }))`
|
|
211
214
|
from `@jskit-ai/jskit-catalog/browser-tests`, with `server`, `build`, `env`
|
|
212
215
|
or `port` only where its own differ from `node ./bin/server.js`,
|
|
213
|
-
`npm run build` and 4173,
|
|
214
|
-
need more than 20 minutes
|
|
216
|
+
`npm run build` and 4173, `globalTimeout` only for a suite measured to
|
|
217
|
+
need more than 20 minutes, and `actionTimeout` or `navigationTimeout` only
|
|
218
|
+
for an action or page measured to need more than 15 or 30 seconds (a stale
|
|
219
|
+
locator should fail in seconds, not minutes). Settings a config adds (projects, a globalSetup
|
|
215
220
|
that proves the server's identity, `timezoneId`) spread over the result:
|
|
216
221
|
`defineConfig({ ...base, projects, use: { ...base.use, timezoneId } })`. A
|
|
217
222
|
second local Playwright config and any `npm run develop` or `vite`
|
|
@@ -44,11 +44,14 @@ in-page banner that moves the form.
|
|
|
44
44
|
|
|
45
45
|
Use `users/user-administration-server` only when the product needs global or
|
|
46
46
|
workspace-scoped administration beyond self-service settings. It demonstrates
|
|
47
|
-
an app-owned feature package built with `
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
an app-owned feature package built with `defineCrudFeature()` on a CRUD
|
|
48
|
+
contract over users-core's `userProfileTableResource`, while keeping profile
|
|
49
|
+
persistence, and the `users` table's one table resource, in `users-core`: the
|
|
50
|
+
feature names `ownerRepository: "users.profiles"` and serves the table through
|
|
51
|
+
users-core's repository instead of owning it.
|
|
52
|
+
|
|
53
|
+
Do not expose password hashes, provider credentials, or session data through
|
|
54
|
+
the CRUD contract. Apply permission and workspace scope before repository access so
|
|
52
55
|
record existence is not leaked.
|
|
53
56
|
|
|
54
57
|
## Verification
|
|
@@ -101,7 +101,7 @@ normal order is:
|
|
|
101
101
|
|
|
102
102
|
1. confirm the product resource, ownership, operations, and fields
|
|
103
103
|
2. author a migration as normal application source
|
|
104
|
-
3.
|
|
104
|
+
3. define the table resource (`defineTableResource`) and, for screens, its CRUD contract (`defineCrudContract`)
|
|
105
105
|
4. use framework APIs for standard repository/service/action/route mechanics
|
|
106
106
|
5. author product-specific screens from the relevant UI pattern
|
|
107
107
|
6. run migrations and direct verification
|
|
@@ -13,10 +13,15 @@ Take database, surface, access, ownership, operations, and fields from product
|
|
|
13
13
|
intent and current source. Ask when a material choice is missing. Do not
|
|
14
14
|
translate the work into generator options.
|
|
15
15
|
|
|
16
|
-
Read the
|
|
17
|
-
library](/patterns/): `
|
|
18
|
-
`crud/
|
|
19
|
-
for
|
|
16
|
+
Read the package-owned pattern in the [source pattern
|
|
17
|
+
library](/patterns/): `database/table-resource` for the table,
|
|
18
|
+
`crud/crud-contract` for its HTTP surface,
|
|
19
|
+
`crud/crud-feature-package` for the server, and `crud/crud-screen-set`
|
|
20
|
+
for routed UI; the [row
|
|
21
|
+
policies](https://github.com/mobily-enterprises/jskit-ai/blob/main/packages/agent-docs/heuristics/row-policies.md)
|
|
22
|
+
and [child
|
|
23
|
+
CRUDs](https://github.com/mobily-enterprises/jskit-ai/blob/main/packages/agent-docs/heuristics/child-cruds.md)
|
|
24
|
+
heuristics cover those variations.
|
|
20
25
|
|
|
21
26
|
Normal CRUD tables use one non-null integer primary key. Foreign keys are
|
|
22
27
|
single-column; composite unique indexes are business constraints, not
|
|
@@ -26,26 +31,38 @@ Match visibility to real ownership and test allowed plus cross-owner cases.
|
|
|
26
31
|
## Author the resource normally
|
|
27
32
|
|
|
28
33
|
1. Write an immutable app-owned migration.
|
|
29
|
-
2. Define the
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
2. Define the table resource with `defineTableResource()` and its HTTP surface
|
|
35
|
+
with `defineCrudContract()`, which names its `namespace`: the routes',
|
|
36
|
+
messages' and events' public name, never taken from the resource.
|
|
37
|
+
3. Use `defineCrudFeature()` for standard table ownership, service, action,
|
|
38
|
+
permission, and route mechanics.
|
|
32
39
|
4. Customize through `decorateRepository`, `decorateService`,
|
|
33
40
|
`operationLifecycle`, and named `actions`; do not copy the standard CRUD
|
|
34
41
|
repository/action/route stack.
|
|
35
42
|
5. Build routed screens from the matching `http-web`/CRUD UI pattern.
|
|
36
43
|
|
|
37
|
-
The resource is canonical for fields,
|
|
38
|
-
|
|
44
|
+
The table resource is canonical for fields, columns, ownership and search; the
|
|
45
|
+
CRUD contract for operations, validation, transport, messages and route
|
|
46
|
+
parameters. A contract shows and accepts only the fields its `fields` names (stored
|
|
47
|
+
`createdAt` and `updatedAt` show without an entry; `createdAt: { output:
|
|
48
|
+
false }` hides one), so a column added later stays out of every API until a
|
|
49
|
+
contract names it. A CRUD feature's answers follow the same rule: an unnamed
|
|
50
|
+
relationship is left out, and an included record shows only its id, its
|
|
51
|
+
relation's `labelKey` and the fields the including contract lists in that
|
|
52
|
+
field's `output.includeFields` (`{ breed: ["name"] }` in the list shows a
|
|
53
|
+
record it links to, as deep as listed), never another contract's output.
|
|
54
|
+
A package with a CRUD feature lists `crud.contracts` in its descriptor's
|
|
55
|
+
`capabilities.requires`. Do not duplicate their schema or serializers.
|
|
39
56
|
Prefer `useCrudListScreen()`, `useCrudViewScreen()`, and
|
|
40
57
|
`useCrudAddEditScreen()`; use `useCommand()` or `useEndpointResource()` for
|
|
41
58
|
non-standard operations.
|
|
42
59
|
|
|
43
60
|
Add resource service methods with `decorateService`; expose commands such as
|
|
44
|
-
`confirm
|
|
45
|
-
`execute`, and `after` share one
|
|
46
|
-
`standard(nextInput)
|
|
47
|
-
|
|
48
|
-
|
|
61
|
+
`confirm` or `cancel` through named `actions`. With `operationLifecycle`, mutation `before`,
|
|
62
|
+
`execute`, and `after` share one transaction, rerun on deadlock, so they touch
|
|
63
|
+
only the database; `execute` receives `standard(nextInput)`. Outside effects go
|
|
64
|
+
in `afterCommit`, run once after commit, or an outbox when durable.
|
|
65
|
+
Repositories persist; services and hooks orchestrate.
|
|
49
66
|
|
|
50
67
|
## Choose ownership, API access and client access independently
|
|
51
68
|
|
|
@@ -53,37 +70,31 @@ These controls answer different questions:
|
|
|
53
70
|
|
|
54
71
|
| Control | Purpose | Public records | Owner-scoped records |
|
|
55
72
|
| --- | --- | --- | --- |
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
| Feature
|
|
73
|
+
| Table resource `ownership` | Which rows belong to the request scope | `"public"`; no ownership column | `"user"`; real non-null `userId` column |
|
|
74
|
+
| CRUD contract `apiAccess` | Who may call the API | `"public"` | `"authenticated"` |
|
|
75
|
+
| Feature route visibility | Server route/action scope: the table's `ownership` unless the feature names `visibility` | `"public"` | `"user"` |
|
|
59
76
|
| Client `access` | Whether to require bootstrap permissions | Default `"auto"` with no permission requirements | Default `"auto"`; declare permissions when required |
|
|
60
77
|
|
|
61
|
-
`access` accepts `auto`, `always`, or `never`, not `public`.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
78
|
+
`access` accepts `auto`, `always`, or `never`, not `public`. `auto` requires a
|
|
79
|
+
permissions bootstrap only for a screen with permission requirements; `always`
|
|
80
|
+
always does. Do not add authentication or workspace machinery to
|
|
81
|
+
make up for an unneeded `always` override. Server access and ownership
|
|
65
82
|
remain authoritative.
|
|
66
83
|
|
|
67
|
-
The Books source patterns
|
|
68
|
-
|
|
84
|
+
The Books source patterns show user ownership. For a public create/list
|
|
85
|
+
resource, use them with this table resource and CRUD contract:
|
|
69
86
|
|
|
70
87
|
```js
|
|
71
|
-
const bookResource =
|
|
72
|
-
|
|
88
|
+
const bookResource = defineTableResource({
|
|
89
|
+
name: "books",
|
|
73
90
|
tableName: "books",
|
|
74
|
-
|
|
75
|
-
autofilter: "public",
|
|
76
|
-
crudOperations: ["list", "view", "create"],
|
|
91
|
+
ownership: "public",
|
|
77
92
|
schema: {
|
|
78
93
|
title: {
|
|
79
94
|
type: "string",
|
|
80
95
|
required: true,
|
|
81
96
|
minLength: 1,
|
|
82
|
-
maxLength: 255
|
|
83
|
-
operations: {
|
|
84
|
-
output: { required: true },
|
|
85
|
-
create: { required: true }
|
|
86
|
-
}
|
|
97
|
+
maxLength: 255
|
|
87
98
|
}
|
|
88
99
|
},
|
|
89
100
|
searchSchema: {
|
|
@@ -96,20 +107,27 @@ const bookResource = defineCrudResource({
|
|
|
96
107
|
}
|
|
97
108
|
}
|
|
98
109
|
});
|
|
110
|
+
|
|
111
|
+
const bookCrud = defineCrudContract(bookResource, {
|
|
112
|
+
namespace: "books",
|
|
113
|
+
apiAccess: "public",
|
|
114
|
+
operations: ["list", "view", "create"],
|
|
115
|
+
fields: { title: { output: "required", create: "required" } }
|
|
116
|
+
});
|
|
99
117
|
```
|
|
100
118
|
|
|
101
|
-
Import `
|
|
102
|
-
`@jskit-ai/resource-crud-core/shared/
|
|
103
|
-
creates `books` with an integer
|
|
104
|
-
`userId` and its foreign key
|
|
105
|
-
in the
|
|
106
|
-
response uses the
|
|
107
|
-
`list`, `view` and `create`; do not invent output-operation aliases.
|
|
108
|
-
|
|
109
|
-
Bind `
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
`crud/
|
|
119
|
+
Import `defineTableResource` from `@jskit-ai/resource-core/shared/tableResource`
|
|
120
|
+
and `defineCrudContract` from `@jskit-ai/resource-crud-core/shared/crudContract`.
|
|
121
|
+
Its migration creates `books` with an integer `id` and non-null `title`; omit
|
|
122
|
+
`userId` and its foreign key.
|
|
123
|
+
`list` requires `view` in the CRUD contract, even when the UI has no detail
|
|
124
|
+
page. A create response uses the CRUD contract's output. Keep operation names
|
|
125
|
+
such as `list`, `view` and `create`; do not invent output-operation aliases.
|
|
126
|
+
|
|
127
|
+
Bind `defineCrudFeature()` with `crud: bookCrud`, `relativePath: "/books"`
|
|
128
|
+
and the application's configured public surface; the table's `"public"`
|
|
129
|
+
ownership makes its routes public. Keep the existing package/provider and migration registration from
|
|
130
|
+
`crud/crud-feature-package`.
|
|
113
131
|
|
|
114
132
|
For the list, keep `BookListPage.vue` thin: use `resourceNamespace: "books"`
|
|
115
133
|
in `useCrudListScreen()` on the configured public surface, remove unsupported
|