@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
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
<!-- Generated from `packages/agent-docs/heuristics/crud-repository-mapping.md`. -->
|
|
2
|
-
|
|
3
|
-
# CRUD Repository Mapping
|
|
4
|
-
|
|
5
|
-
Use when:
|
|
6
|
-
- a CRUD resource needs explicit DB column mapping
|
|
7
|
-
- a field should exist in API output but is not a real DB column
|
|
8
|
-
- the user mentions computed fields or projections
|
|
9
|
-
|
|
10
|
-
Default JSKIT pattern:
|
|
11
|
-
1. Treat the schema as the API contract only.
|
|
12
|
-
2. Put persistence mapping on the field definitions themselves.
|
|
13
|
-
3. Use `actualField` for explicit DB column overrides.
|
|
14
|
-
4. Let generic CRUD runtime handle standard writable `date-time` fields automatically at the DB write seam.
|
|
15
|
-
5. Use `storage.writeSerializer` only for non-default write serialization.
|
|
16
|
-
6. Use `storage: { virtual: true }` for computed output fields.
|
|
17
|
-
7. For `createCrudResourceRuntime(...)` repositories, register computed SQL projections once with `virtualFields`.
|
|
18
|
-
8. For internal JSON REST repositories, register SQL-selected output fields with `createJsonRestResourceScopeOptions(resource, { queryFields })`.
|
|
19
|
-
9. Let generic read paths apply those projections automatically.
|
|
20
|
-
|
|
21
|
-
Field metadata rules:
|
|
22
|
-
- for a normal override, write:
|
|
23
|
-
|
|
24
|
-
```js
|
|
25
|
-
createdAt: {
|
|
26
|
-
type: "dateTime",
|
|
27
|
-
required: true,
|
|
28
|
-
actualField: "created_at"
|
|
29
|
-
}
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
- for a non-default write serializer override, write:
|
|
33
|
-
|
|
34
|
-
```js
|
|
35
|
-
arrivalDatetime: {
|
|
36
|
-
type: "dateTime",
|
|
37
|
-
required: true,
|
|
38
|
-
storage: {
|
|
39
|
-
writeSerializer: "datetime-utc"
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
- for a computed output field, write:
|
|
45
|
-
|
|
46
|
-
```js
|
|
47
|
-
remainingBatchWeight: {
|
|
48
|
-
type: "number",
|
|
49
|
-
required: true,
|
|
50
|
-
storage: {
|
|
51
|
-
virtual: true
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
- omit `actualField`/`storage` when the field is column-backed and the default snake_case mapping is correct
|
|
57
|
-
- standard writable `format: "date-time"` fields do not need explicit `storage.writeSerializer`
|
|
58
|
-
- use `storage.writeSerializer` only for non-default DB write behavior
|
|
59
|
-
- `storage: { virtual: true }` cannot also define `actualField` or `storage.column`
|
|
60
|
-
- `storage: { virtual: true }` cannot also define `storage.writeSerializer`
|
|
61
|
-
- `storage: { virtual: true }` fields must not appear in create/patch write schemas
|
|
62
|
-
|
|
63
|
-
Repository pattern:
|
|
64
|
-
- build the runtime with `createCrudResourceRuntime(resource, knex, { ... })`, passing the application's Knex instance
|
|
65
|
-
- register computed projections in `virtualFields`
|
|
66
|
-
- keep SQL in the repository, not in shared metadata
|
|
67
|
-
- let generic CRUD runtime apply `storage.writeSerializer` during create/update writes
|
|
68
|
-
|
|
69
|
-
Example:
|
|
70
|
-
|
|
71
|
-
```js
|
|
72
|
-
const repositoryRuntime = createCrudResourceRuntime(resource, knex, {
|
|
73
|
-
context: "receivals repository",
|
|
74
|
-
list: LIST_CONFIG,
|
|
75
|
-
virtualFields: {
|
|
76
|
-
remainingBatchWeight: {
|
|
77
|
-
applyProjection(dbQuery, { knex, tableName, alias }) {
|
|
78
|
-
const { sql, bindings } = getRemainingBatchWeightSqlParts({ tableName });
|
|
79
|
-
dbQuery.select(knex.raw(`${sql} as ??`, [...bindings, alias]));
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Internal JSON REST pattern:
|
|
87
|
-
- keep the API field in the resource schema as `storage: { virtual: true }`
|
|
88
|
-
- pass server-only SQL projection callbacks through `createJsonRestResourceScopeOptions(...)`
|
|
89
|
-
- keep query projection SQL in the provider/server registration file, not in browser-imported page code
|
|
90
|
-
|
|
91
|
-
Example:
|
|
92
|
-
|
|
93
|
-
```js
|
|
94
|
-
await addResourceIfMissing(
|
|
95
|
-
api,
|
|
96
|
-
"receivals",
|
|
97
|
-
createJsonRestResourceScopeOptions(resource, {
|
|
98
|
-
queryFields: {
|
|
99
|
-
remainingProcessableWeight: {
|
|
100
|
-
type: "number",
|
|
101
|
-
select({ knex, column }) {
|
|
102
|
-
return knex.raw("?? - coalesce(??, 0)", [
|
|
103
|
-
column("received_weight"),
|
|
104
|
-
column("processed_weight")
|
|
105
|
-
]);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
})
|
|
110
|
-
);
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
If the resource module is server-only, a field may also declare `storage.queryProjection`; `createJsonRestResourceScopeOptions(...)` moves it into JSON REST `queryFields` and removes the virtual field from the storage schema. Prefer the `queryFields` option when the resource module is shared with client code.
|
|
114
|
-
|
|
115
|
-
What CRUD core does for you:
|
|
116
|
-
- default select columns include only column-backed output fields
|
|
117
|
-
- create/update write payloads serialize standard writable `date-time` fields centrally
|
|
118
|
-
- database reads serialize `date`, `time`, and `dateTime` values to their
|
|
119
|
-
strict `json-rest-schema` string contracts before resource validation,
|
|
120
|
-
respecting `temporalPrecision`
|
|
121
|
-
- create/update write payloads also apply any explicit field write serializers centrally
|
|
122
|
-
- `list`, `findById`, `listByIds`, and `listByForeignIds` apply registered virtual projections automatically
|
|
123
|
-
- search and parent-filter fallback derivation only use column-backed fields
|
|
124
|
-
- `listByIds(..., { valueKey })` requires that `valueKey` be column-backed
|
|
125
|
-
|
|
126
|
-
Avoid:
|
|
127
|
-
- manual `clearSelect()` / re-select hacks in individual repository methods just to add computed fields
|
|
128
|
-
- putting SQL fragments or joins into shared field metadata
|
|
129
|
-
- inventing `storage` modes beyond the documented `virtual` contract
|
|
130
|
-
|
|
131
|
-
Review checks:
|
|
132
|
-
- schema defines contract, not storage
|
|
133
|
-
- field definitions own mapping
|
|
134
|
-
- standard datetime DB write serialization stays automatic and runtime-owned
|
|
135
|
-
- any non-default DB write serialization lives in `storage.writeSerializer`, not in per-repository hooks
|
|
136
|
-
- computed fields use `storage: { virtual: true }`
|
|
137
|
-
- repository runtime registers matching `virtualFields`, or the JSON REST provider registers matching `queryFields`
|
|
138
|
-
- no per-method projection duplication when generic CRUD reads already cover the field
|
|
139
|
-
- no JavaScript `Date` object crosses a strict resource validation boundary;
|
|
140
|
-
custom repositories return ISO/RFC 3339 strings explicitly
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { defineCrudJsonApiFeature } from "@jskit-ai/crud-core/server/defineCrudJsonApiFeature";
|
|
2
|
-
import { bookResource } from "../shared/bookResource.js";
|
|
3
|
-
|
|
4
|
-
const BooksFeature = defineCrudJsonApiFeature({
|
|
5
|
-
resource: bookResource,
|
|
6
|
-
id: "app.books",
|
|
7
|
-
capability: "app.books",
|
|
8
|
-
surface: "app",
|
|
9
|
-
ownershipFilter: "user",
|
|
10
|
-
relativePath: "/books"
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
export { BooksFeature };
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { defineCrudResource } from "@jskit-ai/resource-crud-core/shared/crudResource";
|
|
2
|
-
|
|
3
|
-
const bookResource = defineCrudResource({
|
|
4
|
-
namespace: "books",
|
|
5
|
-
tableName: "books",
|
|
6
|
-
apiAccess: "authenticated",
|
|
7
|
-
autofilter: "user",
|
|
8
|
-
schema: {
|
|
9
|
-
userId: {
|
|
10
|
-
type: "id",
|
|
11
|
-
required: true,
|
|
12
|
-
hidden: true,
|
|
13
|
-
operations: {}
|
|
14
|
-
},
|
|
15
|
-
title: {
|
|
16
|
-
type: "string",
|
|
17
|
-
maxLength: 255,
|
|
18
|
-
required: true,
|
|
19
|
-
search: true,
|
|
20
|
-
operations: {
|
|
21
|
-
output: { required: true },
|
|
22
|
-
create: { required: true },
|
|
23
|
-
patch: { required: false }
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
author: {
|
|
27
|
-
type: "string",
|
|
28
|
-
maxLength: 255,
|
|
29
|
-
required: true,
|
|
30
|
-
search: true,
|
|
31
|
-
operations: {
|
|
32
|
-
output: { required: true },
|
|
33
|
-
create: { required: true },
|
|
34
|
-
patch: { required: false }
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
notes: {
|
|
38
|
-
type: "string",
|
|
39
|
-
maxLength: 65535,
|
|
40
|
-
nullable: true,
|
|
41
|
-
search: true,
|
|
42
|
-
operations: {
|
|
43
|
-
output: { required: true },
|
|
44
|
-
create: { required: false },
|
|
45
|
-
patch: { required: false }
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
createdAt: {
|
|
49
|
-
type: "dateTime",
|
|
50
|
-
temporalPrecision: 0,
|
|
51
|
-
default: "now()",
|
|
52
|
-
storage: { writeSerializer: "datetime-utc" },
|
|
53
|
-
operations: { output: { required: true } }
|
|
54
|
-
},
|
|
55
|
-
updatedAt: {
|
|
56
|
-
type: "dateTime",
|
|
57
|
-
temporalPrecision: 0,
|
|
58
|
-
default: "now()",
|
|
59
|
-
storage: { writeSerializer: "datetime-utc" },
|
|
60
|
-
operations: { output: { required: true } }
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
searchSchema: {
|
|
64
|
-
id: { type: "id", actualField: "id" },
|
|
65
|
-
q: {
|
|
66
|
-
type: "string",
|
|
67
|
-
oneOf: ["title", "author", "notes"],
|
|
68
|
-
filterOperator: "like",
|
|
69
|
-
splitBy: " ",
|
|
70
|
-
matchAll: true
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
defaultSort: ["-createdAt"],
|
|
74
|
-
messages: {
|
|
75
|
-
validation: "Fix invalid values and try again.",
|
|
76
|
-
saveSuccess: "Book saved.",
|
|
77
|
-
saveError: "Unable to save this book.",
|
|
78
|
-
deleteSuccess: "Book deleted.",
|
|
79
|
-
deleteError: "Unable to delete this book."
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
export { bookResource };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { bookResource } from "./bookResource.js";
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
id: crud/resource-contract
|
|
3
|
-
title: Owner-scoped CRUD resource contract
|
|
4
|
-
summary: Define an authenticated application resource whose records belong to the current user.
|
|
5
|
-
keywords: authenticated, crud, database, owner-scoped, resource, user
|
|
6
|
-
requires: @jskit-ai/resource-crud-core
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Owner-scoped CRUD resource contract
|
|
10
|
-
|
|
11
|
-
## Use when
|
|
12
|
-
|
|
13
|
-
Use this pattern when an authenticated application stores records that belong
|
|
14
|
-
to one user and standard list, view, create, patch, and delete behaviour fits
|
|
15
|
-
the product.
|
|
16
|
-
|
|
17
|
-
The example is intentionally a normal source file. Copy it directly when the
|
|
18
|
-
shape fits, adapt its fields and language for another entity, or read it as a
|
|
19
|
-
reference for `defineCrudResource()`.
|
|
20
|
-
|
|
21
|
-
## Do not use when
|
|
22
|
-
|
|
23
|
-
Do not use this pattern for public records, workspace-owned records, inherited
|
|
24
|
-
ownership, composite identities, or a domain whose operations do not match
|
|
25
|
-
normal CRUD behaviour. Select a pattern matching the real ownership and
|
|
26
|
-
operation model instead of changing `autofilter` merely to make a test pass.
|
|
27
|
-
|
|
28
|
-
## Product decisions
|
|
29
|
-
|
|
30
|
-
Know these decisions before adapting the example:
|
|
31
|
-
|
|
32
|
-
- whether records truly belong to the current user
|
|
33
|
-
- which fields are required, optional, nullable, searchable, or hidden
|
|
34
|
-
- whether a value is free text or a constrained vocabulary
|
|
35
|
-
- which operations the product actually exposes
|
|
36
|
-
- the user-facing success and failure language
|
|
37
|
-
|
|
38
|
-
Those decisions come from the product conversation or existing application,
|
|
39
|
-
not from a JSKIT questionnaire.
|
|
40
|
-
|
|
41
|
-
## Invariants
|
|
42
|
-
|
|
43
|
-
- The persisted table has a normal single-column primary key.
|
|
44
|
-
- User ownership is represented by a non-null `user_id` column.
|
|
45
|
-
- The resource's hidden `userId` field maps to that ownership column.
|
|
46
|
-
- `autofilter: "user"` is used only for genuine current-user ownership.
|
|
47
|
-
- Each product field's output, create, and patch participation is its
|
|
48
|
-
`operations` block, or the default `defineCrudResource()` gives a field
|
|
49
|
-
without one (see Field operations and lookups below).
|
|
50
|
-
- Temporal database values use the declared storage serializer.
|
|
51
|
-
- The source-controlled migration and resource contract agree.
|
|
52
|
-
- Server policy tests prove that one user cannot read or mutate another user's
|
|
53
|
-
records.
|
|
54
|
-
|
|
55
|
-
## Framework APIs
|
|
56
|
-
|
|
57
|
-
The example uses `defineCrudResource()` from
|
|
58
|
-
`@jskit-ai/resource-crud-core/shared/crudResource`. That API derives the normal
|
|
59
|
-
CRUD operation validators from one readable field contract.
|
|
60
|
-
|
|
61
|
-
Higher layers should consume this resource through JSKIT's CRUD service,
|
|
62
|
-
repository, route, and UI APIs. They should not rebuild field validators or
|
|
63
|
-
serialize requests independently.
|
|
64
|
-
|
|
65
|
-
## Field operations and lookups
|
|
66
|
-
|
|
67
|
-
A field's `operations` name the operations it takes part in: `output` (the
|
|
68
|
-
record in every response), and `create` and `patch` (the request bodies; an
|
|
69
|
-
enabled `replace` uses the `create` fields unless a field names `replace`). A
|
|
70
|
-
field that leaves `operations` out takes the default:
|
|
71
|
-
|
|
72
|
-
| Field | Default `operations` |
|
|
73
|
-
| --- | --- |
|
|
74
|
-
| `primary: true` | `{}`: no operation; every record output already carries its `id` |
|
|
75
|
-
| `hidden: true` | `{}`: no operation |
|
|
76
|
-
| `readOnly: true` | `{ output: { required: true } }` |
|
|
77
|
-
| any other field | `{ output: { required: true }, create: { required: <the field's required> }, patch: { required: false } }` |
|
|
78
|
-
|
|
79
|
-
So a `required: true` field is required on create, and any other field is
|
|
80
|
-
optional there. An `operations` block you write always wins, for example
|
|
81
|
-
`operations: { output: {}, create: {}, patch: {} }` for a field that is
|
|
82
|
-
optional everywhere. Write the block only where it differs from the default:
|
|
83
|
-
|
|
84
|
-
```js
|
|
85
|
-
schema: {
|
|
86
|
-
userId: { type: "id", required: true, hidden: true },
|
|
87
|
-
title: { type: "string", maxLength: 255, required: true, search: true },
|
|
88
|
-
notes: { type: "string", maxLength: 65535, nullable: true, search: true },
|
|
89
|
-
createdAt: { type: "dateTime", default: "now()", readOnly: true, storage: { writeSerializer: "datetime-utc" } }
|
|
90
|
-
}
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
A field that looks up a record of another resource can name that resource's
|
|
94
|
-
namespace with `lookup`:
|
|
95
|
-
|
|
96
|
-
```js
|
|
97
|
-
shelfId: { type: "id", nullable: true, search: true, lookup: "shelves" }
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
`defineCrudResource()` expands it, where `lookup` stood, to:
|
|
101
|
-
|
|
102
|
-
```js
|
|
103
|
-
relation: { kind: "lookup", namespace: "shelves", valueKey: "id" },
|
|
104
|
-
belongsTo: "shelves", // the namespace's JSON REST resource, camel-cased
|
|
105
|
-
as: "shelf", // the field key without its Id suffix
|
|
106
|
-
ui: { formControl: "autocomplete" }
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
Keys you write beside `lookup` win: `belongsTo` and `as` replace the
|
|
110
|
-
filled-in values, and `relation` and `ui` are merged over them, so
|
|
111
|
-
`relation: { labelKey: "name" }` adds a label key and
|
|
112
|
-
`ui: { formControl: "select" }` picks a select. A field whose key has no `Id`
|
|
113
|
-
suffix must write `as`. The built resource equals one that writes the expanded
|
|
114
|
-
keys out.
|
|
115
|
-
|
|
116
|
-
## Example files
|
|
117
|
-
|
|
118
|
-
- `example/bookResource.js` is a complete resource contract for personal book
|
|
119
|
-
records.
|
|
120
|
-
|
|
121
|
-
The example deliberately uses a concrete domain. Rename and edit normal source
|
|
122
|
-
rather than feeding it through placeholder interpolation.
|
|
123
|
-
|
|
124
|
-
## Variation points
|
|
125
|
-
|
|
126
|
-
Safe adaptations include:
|
|
127
|
-
|
|
128
|
-
- namespace and table name
|
|
129
|
-
- product fields and validation
|
|
130
|
-
- searchable fields and default sort
|
|
131
|
-
- enabled CRUD operations
|
|
132
|
-
- user-facing messages
|
|
133
|
-
- lookup relationships supported by the resource APIs
|
|
134
|
-
|
|
135
|
-
Changing ownership, access, identity, or persistence architecture selects a
|
|
136
|
-
different pattern and requires corresponding policy and migration evidence.
|
|
137
|
-
|
|
138
|
-
## Verification
|
|
139
|
-
|
|
140
|
-
- Import the resource and prove all intended operations exist.
|
|
141
|
-
- Validate representative accepted and rejected field values.
|
|
142
|
-
- Rebuild the table from source-controlled migrations in a disposable database.
|
|
143
|
-
- Run positive current-user CRUD tests.
|
|
144
|
-
- Run negative cross-user read and mutation tests.
|
|
145
|
-
- Run the application's focused tests and broad verifier at sign-off.
|
|
146
|
-
|
|
147
|
-
## Avoid
|
|
148
|
-
|
|
149
|
-
- generator provenance or scaffold-shape metadata
|
|
150
|
-
- a field-by-field questionnaire
|
|
151
|
-
- a live table as the only source of schema truth
|
|
152
|
-
- app-specific request serializers that duplicate the resource contract
|
|
153
|
-
- hidden ownership aliases other than the framework's explicit ownership field
|
|
154
|
-
- durable receipts recording that this pattern was copied
|
|
155
|
-
- changing an installed baseline migration instead of adding a new migration
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { defineCrudResource } from "@jskit-ai/resource-crud-core/shared/crudResource";
|
|
2
|
-
|
|
3
|
-
const bookResource = defineCrudResource({
|
|
4
|
-
namespace: "books",
|
|
5
|
-
tableName: "books",
|
|
6
|
-
apiAccess: "authenticated",
|
|
7
|
-
schema: {
|
|
8
|
-
userId: {
|
|
9
|
-
type: "id",
|
|
10
|
-
required: true,
|
|
11
|
-
hidden: true,
|
|
12
|
-
operations: {}
|
|
13
|
-
},
|
|
14
|
-
title: {
|
|
15
|
-
type: "string",
|
|
16
|
-
maxLength: 255,
|
|
17
|
-
required: true,
|
|
18
|
-
search: true,
|
|
19
|
-
operations: {
|
|
20
|
-
output: { required: true },
|
|
21
|
-
create: { required: true },
|
|
22
|
-
patch: { required: false }
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
author: {
|
|
26
|
-
type: "string",
|
|
27
|
-
maxLength: 255,
|
|
28
|
-
required: true,
|
|
29
|
-
search: true,
|
|
30
|
-
operations: {
|
|
31
|
-
output: { required: true },
|
|
32
|
-
create: { required: true },
|
|
33
|
-
patch: { required: false }
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
notes: {
|
|
37
|
-
type: "string",
|
|
38
|
-
maxLength: 65535,
|
|
39
|
-
nullable: true,
|
|
40
|
-
search: true,
|
|
41
|
-
operations: {
|
|
42
|
-
output: { required: true },
|
|
43
|
-
create: { required: false },
|
|
44
|
-
patch: { required: false }
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
createdAt: {
|
|
48
|
-
type: "dateTime",
|
|
49
|
-
temporalPrecision: 0,
|
|
50
|
-
default: "now()",
|
|
51
|
-
storage: { writeSerializer: "datetime-utc" },
|
|
52
|
-
operations: {
|
|
53
|
-
output: { required: true }
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
updatedAt: {
|
|
57
|
-
type: "dateTime",
|
|
58
|
-
temporalPrecision: 0,
|
|
59
|
-
default: "now()",
|
|
60
|
-
storage: { writeSerializer: "datetime-utc" },
|
|
61
|
-
operations: {
|
|
62
|
-
output: { required: true }
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
searchSchema: {
|
|
67
|
-
id: { type: "id", actualField: "id" },
|
|
68
|
-
q: {
|
|
69
|
-
type: "string",
|
|
70
|
-
oneOf: ["title", "author", "notes"],
|
|
71
|
-
filterOperator: "like",
|
|
72
|
-
splitBy: " ",
|
|
73
|
-
matchAll: true
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
defaultSort: ["-createdAt"],
|
|
77
|
-
autofilter: "user",
|
|
78
|
-
messages: {
|
|
79
|
-
validation: "Fix invalid values and try again.",
|
|
80
|
-
saveSuccess: "Book saved.",
|
|
81
|
-
saveError: "Unable to save this book.",
|
|
82
|
-
deleteSuccess: "Book deleted.",
|
|
83
|
-
deleteError: "Unable to delete this book."
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
export { bookResource };
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { createWithTransaction } from "@jskit-ai/database-runtime/shared";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* The invoice rollup's persistence: a reviewed custom Knex repository.
|
|
5
|
-
* @param {{ knex: import("@jskit-ai/database-runtime/server/types").RuntimeKnex }} dependencies
|
|
6
|
-
*/
|
|
7
|
-
function createInvoiceRollup({ knex }) {
|
|
8
|
-
const withTransaction = createWithTransaction(knex);
|
|
9
|
-
|
|
10
|
-
async function getStatus(input, options = {}) {
|
|
11
|
-
return {
|
|
12
|
-
ok: true,
|
|
13
|
-
feature: "invoice-rollup",
|
|
14
|
-
persistence: "custom-knex",
|
|
15
|
-
tableName: "invoice_rollups",
|
|
16
|
-
hasTransaction: Boolean(options.trx),
|
|
17
|
-
input
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async function execute(input, options = {}) {
|
|
22
|
-
return {
|
|
23
|
-
accepted: false,
|
|
24
|
-
feature: "invoice-rollup",
|
|
25
|
-
persistence: "custom-knex",
|
|
26
|
-
tableName: "invoice_rollups",
|
|
27
|
-
hasTransaction: Boolean(options.trx),
|
|
28
|
-
input,
|
|
29
|
-
message: "Replace this example with the reviewed domain-specific query."
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return Object.freeze({
|
|
34
|
-
withTransaction,
|
|
35
|
-
getStatus,
|
|
36
|
-
execute
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export { createInvoiceRollup };
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { defineCrudResource } from "@jskit-ai/resource-crud-core/shared/crudResource";
|
|
2
|
-
|
|
3
|
-
const resource = defineCrudResource({
|
|
4
|
-
namespace: "users",
|
|
5
|
-
tableName: "users",
|
|
6
|
-
crudOperations: ["list", "view"],
|
|
7
|
-
schema: {
|
|
8
|
-
name: {
|
|
9
|
-
type: "string",
|
|
10
|
-
required: true,
|
|
11
|
-
minLength: 1,
|
|
12
|
-
maxLength: 160,
|
|
13
|
-
actualField: "display_name",
|
|
14
|
-
search: true,
|
|
15
|
-
operations: {
|
|
16
|
-
output: { required: false }
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
email: {
|
|
20
|
-
type: "string",
|
|
21
|
-
required: true,
|
|
22
|
-
search: true,
|
|
23
|
-
operations: {
|
|
24
|
-
output: { required: false }
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
username: {
|
|
28
|
-
type: "string",
|
|
29
|
-
required: true,
|
|
30
|
-
search: true,
|
|
31
|
-
operations: {
|
|
32
|
-
output: { required: false }
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
createdAt: {
|
|
36
|
-
type: "dateTime",
|
|
37
|
-
required: true,
|
|
38
|
-
storage: { writeSerializer: "datetime-utc" },
|
|
39
|
-
operations: {
|
|
40
|
-
output: { required: true }
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
updatedAt: {
|
|
44
|
-
type: "dateTime",
|
|
45
|
-
required: true,
|
|
46
|
-
storage: { writeSerializer: "datetime-utc" },
|
|
47
|
-
operations: {
|
|
48
|
-
output: { required: true }
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
searchSchema: {
|
|
53
|
-
id: {
|
|
54
|
-
type: "id",
|
|
55
|
-
actualField: "id"
|
|
56
|
-
},
|
|
57
|
-
q: {
|
|
58
|
-
type: "string",
|
|
59
|
-
oneOf: ["name", "email", "username"],
|
|
60
|
-
filterOperator: "like",
|
|
61
|
-
splitBy: " ",
|
|
62
|
-
matchAll: true
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
defaultSort: ["name", "email"],
|
|
66
|
-
autofilter: "public"
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
export { resource };
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { defineCrudResource } from "@jskit-ai/resource-crud-core/shared/crudResource";
|
|
2
|
-
|
|
3
|
-
const resource = defineCrudResource({
|
|
4
|
-
namespace: "users",
|
|
5
|
-
tableName: "users",
|
|
6
|
-
crudOperations: ["list", "view"],
|
|
7
|
-
schema: {
|
|
8
|
-
name: {
|
|
9
|
-
type: "string",
|
|
10
|
-
required: true,
|
|
11
|
-
minLength: 1,
|
|
12
|
-
maxLength: 160,
|
|
13
|
-
actualField: "display_name",
|
|
14
|
-
search: true,
|
|
15
|
-
operations: { output: { required: false } }
|
|
16
|
-
},
|
|
17
|
-
email: {
|
|
18
|
-
type: "string",
|
|
19
|
-
required: true,
|
|
20
|
-
search: true,
|
|
21
|
-
operations: { output: { required: false } }
|
|
22
|
-
},
|
|
23
|
-
username: {
|
|
24
|
-
type: "string",
|
|
25
|
-
required: true,
|
|
26
|
-
search: true,
|
|
27
|
-
operations: { output: { required: false } }
|
|
28
|
-
},
|
|
29
|
-
createdAt: {
|
|
30
|
-
type: "dateTime",
|
|
31
|
-
required: true,
|
|
32
|
-
storage: { writeSerializer: "datetime-utc" },
|
|
33
|
-
operations: { output: { required: true } }
|
|
34
|
-
},
|
|
35
|
-
updatedAt: {
|
|
36
|
-
type: "dateTime",
|
|
37
|
-
required: true,
|
|
38
|
-
storage: { writeSerializer: "datetime-utc" },
|
|
39
|
-
operations: { output: { required: true } }
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
searchSchema: {
|
|
43
|
-
id: { type: "id", actualField: "id" },
|
|
44
|
-
q: {
|
|
45
|
-
type: "string",
|
|
46
|
-
oneOf: ["name", "email", "username"],
|
|
47
|
-
filterOperator: "like",
|
|
48
|
-
splitBy: " ",
|
|
49
|
-
matchAll: true
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
defaultSort: ["name", "email"],
|
|
53
|
-
autofilter: "public"
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
export { resource };
|
|
File without changes
|