@jskit-ai/agent-docs 0.1.283 → 0.1.285
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 +76 -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 +134 -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 +17 -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 +1207 -0
- package/guide/agent/framework/sanity-checks.md +26 -27
- package/guide/agent/framework/typed-features.md +117 -248
- 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 +3 -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 +69 -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 +45 -175
- package/reference/autogen/packages/database-runtime-mysql.md +0 -26
- package/reference/autogen/packages/database-runtime.md +11 -33
- package/reference/autogen/packages/http-runtime.md +3 -3
- package/reference/autogen/packages/http-web.md +12 -0
- package/reference/autogen/packages/kernel.md +27 -2
- package/reference/autogen/packages/oidc-issuer-core.md +4 -0
- package/reference/autogen/packages/payments-core.md +28 -6
- package/reference/autogen/packages/realtime.md +15 -6
- package/reference/autogen/packages/resource-core.md +32 -5
- package/reference/autogen/packages/resource-crud-core.md +30 -34
- package/reference/autogen/packages/resource-runtime.md +220 -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 +33 -14
- package/reference/autogen/packages/workspaces-core.md +17 -10
- package/reference/autogen/tooling/config-eslint.md +74 -0
- package/reference/autogen/tooling/jskit-catalog.md +50 -1
- package/reference/autogen/tooling/jskit-release.md +5 -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 +1202 -0
- package/skills/jskit/references/database-layer.md +411 -0
- package/skills/jskit/references/existing-application-migration.md +134 -72
- package/skills/jskit/references/heuristics/INDEX.md +6 -4
- package/skills/jskit/references/heuristics/crud-authoring.md +26 -15
- 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 +120 -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 +63 -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 +117 -248
- 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
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
"./shared": "./src/shared/index.js"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@jskit-ai/crud-core": "0.2.
|
|
14
|
-
"@jskit-ai/resource-crud-core": "0.1.
|
|
13
|
+
"@jskit-ai/crud-core": "0.2.16",
|
|
14
|
+
"@jskit-ai/resource-crud-core": "0.1.206",
|
|
15
|
+
"@jskit-ai/users-core": "0.2.17"
|
|
15
16
|
},
|
|
16
17
|
"jskit": {
|
|
17
18
|
"kind": "runtime",
|
|
@@ -20,10 +21,10 @@
|
|
|
20
21
|
"app.users"
|
|
21
22
|
],
|
|
22
23
|
"requires": [
|
|
24
|
+
"crud.contracts",
|
|
23
25
|
"runtime.http",
|
|
24
|
-
"runtime.json-rest-api",
|
|
25
26
|
"runtime.actions",
|
|
26
|
-
"
|
|
27
|
+
"users.profiles"
|
|
27
28
|
]
|
|
28
29
|
},
|
|
29
30
|
"runtime": {
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { defineCrudFeature } from "@jskit-ai/crud-core/server/defineCrudFeature";
|
|
2
|
+
import { userCrud } from "../shared/userCrud.js";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
// users-core owns the users table; this feature serves the administration
|
|
5
|
+
// CRUD contract through its owner's repository.
|
|
6
|
+
const UsersFeature = defineCrudFeature({
|
|
7
|
+
crud: userCrud,
|
|
8
|
+
ownerRepository: "users.profiles",
|
|
6
9
|
id: "app.users",
|
|
7
10
|
capability: "app.users",
|
|
8
11
|
surface: "home",
|
|
9
|
-
ownershipFilter: "public",
|
|
10
12
|
relativePath: "/users"
|
|
11
13
|
});
|
|
12
14
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { userCrud } from "./userCrud.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { defineCrudContract } from "@jskit-ai/resource-crud-core/shared/crudContract";
|
|
2
|
+
import { userProfileTableResource } from "@jskit-ai/users-core/shared/resources/userProfileResource";
|
|
3
|
+
|
|
4
|
+
// The users list and view. The users table has one table resource, users-core's
|
|
5
|
+
// userProfileTableResource; this contract chooses the operations and the fields they
|
|
6
|
+
// return, and the list searches with the resource's q (display name, email and
|
|
7
|
+
// username). It shows only the fields it names, so the sign-in identity and the
|
|
8
|
+
// avatar's storage stay out of administration.
|
|
9
|
+
const userCrud = defineCrudContract(userProfileTableResource, {
|
|
10
|
+
namespace: "users",
|
|
11
|
+
operations: ["list", "view"],
|
|
12
|
+
fields: {
|
|
13
|
+
displayName: { output: "optional" },
|
|
14
|
+
email: { output: "optional" },
|
|
15
|
+
username: { output: "optional" },
|
|
16
|
+
createdAt: { output: "required" },
|
|
17
|
+
updatedAt: { output: "required" }
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export { userCrud };
|
|
@@ -10,9 +10,14 @@
|
|
|
10
10
|
"./shared": "./src/shared/index.js"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@jskit-ai/crud-core": "0.2.
|
|
14
|
-
"@jskit-ai/resource-crud-core": "0.1.
|
|
15
|
-
"@jskit-ai/
|
|
13
|
+
"@jskit-ai/crud-core": "0.2.16",
|
|
14
|
+
"@jskit-ai/resource-crud-core": "0.1.206",
|
|
15
|
+
"@jskit-ai/users-core": "0.2.17",
|
|
16
|
+
"@jskit-ai/workspaces-core": "0.2.17"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@jskit-ai/kernel": "0.2.15",
|
|
20
|
+
"@jskit-ai/resource-runtime": "0.2.13"
|
|
16
21
|
},
|
|
17
22
|
"jskit": {
|
|
18
23
|
"kind": "runtime",
|
|
@@ -21,10 +26,11 @@
|
|
|
21
26
|
"app.users-workspace"
|
|
22
27
|
],
|
|
23
28
|
"requires": [
|
|
29
|
+
"crud.contracts",
|
|
24
30
|
"runtime.http",
|
|
25
|
-
"runtime.json-rest-api",
|
|
26
31
|
"runtime.actions",
|
|
27
|
-
"
|
|
32
|
+
"users.profiles",
|
|
33
|
+
"workspaces.memberships"
|
|
28
34
|
]
|
|
29
35
|
},
|
|
30
36
|
"runtime": {
|
|
@@ -1,13 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineCrudFeature } from "@jskit-ai/crud-core/server/defineCrudFeature";
|
|
2
2
|
import { WORKSPACE_CRUD_SCOPE } from "@jskit-ai/workspaces-core/server/support/workspaceCrud";
|
|
3
|
-
import {
|
|
3
|
+
import { userCrud } from "../shared/userCrud.js";
|
|
4
|
+
import { workspaceMembersOnly } from "./workspaceMembersOnly.js";
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
// users-core owns the users table; this feature serves the administration
|
|
7
|
+
// CRUD contract on a workspace's admin surface through its owner's
|
|
8
|
+
// repository. The routes are a workspace's, so they resolve the caller's
|
|
9
|
+
// workspace and check its crud.users.* permissions, and the repository is
|
|
10
|
+
// narrowed to that workspace's members: the users table is every user's.
|
|
11
|
+
const UsersWorkspaceFeature = defineCrudFeature({
|
|
12
|
+
crud: userCrud,
|
|
13
|
+
ownerRepository: "users.profiles",
|
|
14
|
+
requires: { memberships: "workspaces.memberships" },
|
|
15
|
+
decorateRepository: ({ repository, memberships }) => workspaceMembersOnly(repository, memberships),
|
|
16
|
+
visibility: "workspace",
|
|
7
17
|
id: "app.users-workspace",
|
|
8
18
|
capability: "app.users-workspace",
|
|
9
19
|
surface: "admin",
|
|
10
|
-
ownershipFilter: "workspace",
|
|
11
20
|
relativePath: "/users",
|
|
12
21
|
scope: WORKSPACE_CRUD_SCOPE
|
|
13
22
|
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
|
|
2
|
+
import { passedOn, resolveWorkspaceScopeValue } from "@jskit-ai/resource-runtime/server";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* users-core's users repository narrowed to the caller's workspace: a list reads only the workspace's active members,
|
|
6
|
+
* and anyone else is a missing record, so a workspace admin never learns who else has an account. The users table is
|
|
7
|
+
* public, so its handle alone would list every user; the narrowing reads the workspace's memberships first. It answers
|
|
8
|
+
* only the reads it narrows (query and get, with pageLimits): the rest of users.profiles, such as findByEmail or
|
|
9
|
+
* countUsers, never passes through this feature's repository, so nothing reaches users past the narrowing through it.
|
|
10
|
+
* @param {import("@jskit-ai/crud-core/server/types").CrudRepository} repository
|
|
11
|
+
* @param {import("@jskit-ai/workspaces-core/server/types").WorkspaceMembershipsRepository} memberships
|
|
12
|
+
*/
|
|
13
|
+
function workspaceMembersOnly(repository, memberships) {
|
|
14
|
+
// The caller's workspace's active members; none when the context names no workspace. The memberships get only the
|
|
15
|
+
// context and transaction: the list's own settings (its format, missing) are for the users read.
|
|
16
|
+
function memberIds(options) {
|
|
17
|
+
return memberships.activeMemberIds(resolveWorkspaceScopeValue(options.context), passedOn(options));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* One page of the workspace's members the params select.
|
|
22
|
+
* @param {Record<string, any>} params
|
|
23
|
+
* @param {{ context?: object }} options
|
|
24
|
+
*/
|
|
25
|
+
async function query(params, options) {
|
|
26
|
+
const members = await memberIds(options);
|
|
27
|
+
const requested = params.filters?.id;
|
|
28
|
+
// An empty list matches no row: a workspace with no members lists nobody.
|
|
29
|
+
const id = requested === undefined ? members : [requested].flat().map(String).filter((memberId) => members.includes(memberId));
|
|
30
|
+
return repository.query({ ...params, filters: { ...params.filters, id } }, options);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A member of the workspace, or a missing record for anyone else.
|
|
35
|
+
* @param {unknown} id
|
|
36
|
+
* @param {Record<string, any>} params
|
|
37
|
+
* @param {{ context?: object, missing?: "skip" }} options
|
|
38
|
+
*/
|
|
39
|
+
async function get(id, params, options) {
|
|
40
|
+
if ((await memberIds(options)).includes(String(id))) {
|
|
41
|
+
return repository.get(id, params, options);
|
|
42
|
+
}
|
|
43
|
+
if (options.missing === "skip") {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
throw new AppError(404, "Document not found.", { code: "crud.document.notFound" });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return Object.freeze({ query, get, pageLimits: repository.pageLimits });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export { workspaceMembersOnly };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { userCrud } from "./userCrud.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { defineCrudContract } from "@jskit-ai/resource-crud-core/shared/crudContract";
|
|
2
|
+
import { userProfileTableResource } from "@jskit-ai/users-core/shared/resources/userProfileResource";
|
|
3
|
+
|
|
4
|
+
// The users list and view. The users table has one table resource, users-core's
|
|
5
|
+
// userProfileTableResource; this contract chooses the operations and the fields they
|
|
6
|
+
// return, and the list searches with the resource's q (display name, email and
|
|
7
|
+
// username). It shows only the fields it names, so the sign-in identity and the
|
|
8
|
+
// avatar's storage stay out of administration.
|
|
9
|
+
const userCrud = defineCrudContract(userProfileTableResource, {
|
|
10
|
+
namespace: "users",
|
|
11
|
+
operations: ["list", "view"],
|
|
12
|
+
fields: {
|
|
13
|
+
displayName: { output: "optional" },
|
|
14
|
+
email: { output: "optional" },
|
|
15
|
+
username: { output: "optional" },
|
|
16
|
+
createdAt: { output: "required" },
|
|
17
|
+
updatedAt: { output: "required" }
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export { userCrud };
|
|
@@ -66,20 +66,20 @@ catalogue entries. Updating labels requires no database migration.
|
|
|
66
66
|
Provide one function as the optional `workspaces.invite-acceptance-participant`
|
|
67
67
|
capability, from a small application feature that requires only what its writes
|
|
68
68
|
need (it cannot require `workspaces.core`, which receives it). The built-in
|
|
69
|
-
`workspace.invite.redeem` action invokes `fn({ invite, user },
|
|
69
|
+
`workspace.invite.redeem` action invokes `fn({ invite, user }, txOptions)`
|
|
70
70
|
after validating the recipient, state and expiry and activating membership in the
|
|
71
|
-
uncommitted transaction.
|
|
72
|
-
failures. JSKIT marks accepted and commits after the function succeeds. Keep
|
|
71
|
+
uncommitted transaction. Pass `txOptions` to every application owner's calls and
|
|
72
|
+
propagate failures. JSKIT marks accepted and commits after the function succeeds. Keep
|
|
73
73
|
external effects out of it. Without the capability, acceptance activates the
|
|
74
74
|
membership only. Allow ordinary invitations; reject stale, revoked, conflicting
|
|
75
75
|
or unexpectedly missing application associations.
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
or
|
|
77
|
+
Call `members.prepareInvite(workspace, actor, payload, txOptions)` inside
|
|
78
|
+
`repositories.workspaceInvites.withTransaction(options, async (txOptions) => …)`
|
|
79
|
+
or another resource handle's `withTransaction`. It returns
|
|
80
80
|
`{ createdInviteId, inviteTokenPreview }` without delivery. Lock the workspace
|
|
81
|
-
with `
|
|
82
|
-
|
|
81
|
+
with `lock(workspace.id, txOptions)` of the `workspaces` capability before
|
|
82
|
+
application draft/receipt rows, and save the exact invitation
|
|
83
83
|
association and retry receipt before commit. Failure restores the previous
|
|
84
84
|
pending invitation. Keep access drafts separate: preparation creates a real token.
|
|
85
85
|
|
|
@@ -92,7 +92,7 @@ failure. A provider may accept a message before failing; the application owns
|
|
|
92
92
|
receipts and retries. Keep tokens/URLs out of logs and use controlled mailers in tests.
|
|
93
93
|
|
|
94
94
|
`createInvite`, acceptance and refusal own their managed transactions and
|
|
95
|
-
|
|
95
|
+
refuse a call made inside one (`withTransaction({ ...options, outermost: true }, work)`). Use preparation followed by delivery after commit for
|
|
96
96
|
transactional creation; put related acceptance writes in the participant. Revocation may join
|
|
97
97
|
a managed transaction. Raw Knex/savepoint transactions cannot own resource
|
|
98
98
|
writes. Invitation mutations lock workspace then invitation rows, revalidate
|
|
@@ -54,6 +54,22 @@ the **Your workspaces** page at `src/pages/home/workspaces/index.vue`, member
|
|
|
54
54
|
and settings routes, not-found state, app-owned invitation widgets, and the
|
|
55
55
|
components that an application's client provider registers.
|
|
56
56
|
|
|
57
|
+
`example/src/surfaces/<surface>/` and `example/src/pages/admin/` are each
|
|
58
|
+
surface's own pages; the app puts them under the surface's `pagesRoot`. With
|
|
59
|
+
`pagesRoot: "w/[workspaceSlug]"` for `app` and `"w/[workspaceSlug]/admin"` for
|
|
60
|
+
`admin`:
|
|
61
|
+
|
|
62
|
+
| Example file | App file |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `src/surfaces/app/root.vue` | `src/pages/w/[workspaceSlug].vue` |
|
|
65
|
+
| `src/surfaces/app/index.vue` | `src/pages/w/[workspaceSlug]/index.vue` |
|
|
66
|
+
| `src/surfaces/admin/root.vue` | `src/pages/w/[workspaceSlug]/admin.vue` |
|
|
67
|
+
| `src/surfaces/admin/index.vue` | `src/pages/w/[workspaceSlug]/admin/index.vue` |
|
|
68
|
+
| `src/pages/admin/...` | `src/pages/w/[workspaceSlug]/admin/...` |
|
|
69
|
+
|
|
70
|
+
The surface definitions and access policies they need are in the human
|
|
71
|
+
guide's **Workspace tenancy → Surfaces and where `/` lands**.
|
|
72
|
+
|
|
57
73
|
## Variation points
|
|
58
74
|
|
|
59
75
|
Change surfaces, routes, section owners, labels, icons, placement order,
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
# Sanity checks
|
|
4
4
|
|
|
5
5
|
Before calling a list, a save, a test, a refactor, a screen or a deployment
|
|
6
|
-
done, measure what it
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
done, measure what it costs and does, judge whether that is sane, and fix the
|
|
7
|
+
cause when it is not. This page holds JSKIT's part: the pitfalls JSKIT's own
|
|
8
|
+
packages cause, then the JSKIT call, helper or setting that applies each
|
|
9
|
+
check, with one line on what it catches and why.
|
|
10
10
|
|
|
11
11
|
## JSKIT's own pitfalls
|
|
12
12
|
|
|
@@ -66,36 +66,35 @@ expects the relationship id or an unselected field never gets it.
|
|
|
66
66
|
|
|
67
67
|
### Every row
|
|
68
68
|
|
|
69
|
-
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
- A lookup by a unique key matches at most one row, so it may read `rows[0]`
|
|
77
|
-
of a plain query.
|
|
69
|
+
- A handle's `query()` answers one page and refuses a missing or oversized
|
|
70
|
+
`page.size`, because json-rest-api would otherwise cut the list without an
|
|
71
|
+
error. A read its callers treat as complete uses `queryAll()`, because the
|
|
72
|
+
missing rows otherwise vanish from every picker and delivery built on it
|
|
73
|
+
([Reads](data-access.md#reads-shaped-for-the-screen)).
|
|
74
|
+
- A lookup by a unique key matches at most one row: `get(id)`, or a `query()`
|
|
75
|
+
of page size 1.
|
|
78
76
|
|
|
79
77
|
### Query counts and batches
|
|
80
78
|
|
|
81
|
-
- Count a path's queries with `
|
|
82
|
-
`@jskit-ai/
|
|
79
|
+
- Count a path's queries with `expectFlatQueries()` from
|
|
80
|
+
`@jskit-ai/resource-runtime/test`, or `countQueries()` from
|
|
81
|
+
`@jskit-ai/database-runtime/server/testDatabases`, at a few rows and at a
|
|
83
82
|
realistic number; the count should follow tables and pages, not rows
|
|
84
|
-
([
|
|
85
|
-
sizes](typed-features.md#query-counts-at-realistic-sizes)).
|
|
83
|
+
([Tests](data-access.md#tests)).
|
|
86
84
|
- A JSON:API read adds a few lookup and visibility queries per page and per
|
|
87
|
-
relationship field
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
relationship field, and json-rest-api reads each relationship's related ids
|
|
86
|
+
in one statement however many rows the page has. Both follow pages, not
|
|
87
|
+
rows, so `expectFlatQueries` passes them.
|
|
88
|
+
- A handle's `get()` or `query()`, or a service `get`, inside a loop is the
|
|
89
|
+
usual per-row read; batch it with an id-list filter through `queryAll()` or
|
|
90
|
+
`include`, grouped with `Map.groupBy()`
|
|
91
|
+
([Reads](data-access.md#reads-shaped-for-the-screen)).
|
|
92
92
|
- A `rowPolicy` adds one correlated predicate to the query it is given, so
|
|
93
93
|
its cost follows the rows read, not the subject's history. It runs on every
|
|
94
|
-
single-record read, including a write's default `returning: "
|
|
95
|
-
back, so a write that discards its result passes `returning: "
|
|
96
|
-
a batch
|
|
97
|
-
([
|
|
98
|
-
checks](typed-features.md#bounded-access-checks)).
|
|
94
|
+
single-record read, including a write's default `returning: "record"` read
|
|
95
|
+
back, so a write that discards its result passes `returning: "id"` or
|
|
96
|
+
`"none"`, and a batch uses the many-row writes (`createMany()`,
|
|
97
|
+
`patchMany()`) ([Layer 1](data-access.md#layer-1-the-table-resource)).
|
|
99
98
|
|
|
100
99
|
### Tests
|
|
101
100
|
|
|
@@ -113,7 +112,7 @@ expects the relationship id or an unselected field never gets it.
|
|
|
113
112
|
duration, so slow preparation shows ([Test databases are prepared
|
|
114
113
|
once](typed-features.md#test-databases-are-prepared-once),
|
|
115
114
|
[integration test
|
|
116
|
-
databases](
|
|
115
|
+
databases](database-layer.md#integration-test-databases)).
|
|
117
116
|
|
|
118
117
|
### Inputs and access
|
|
119
118
|
|