@protobase/schema 0.53.3 → 0.53.5
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/dist/access/resolve.js +1 -1
- package/dist/access/restrict.d.ts +1 -1
- package/dist/access/restrict.js +4 -2
- package/dist/model.d.ts +4 -0
- package/dist/resource-model.d.ts +1 -0
- package/dist/resource-model.js +1 -0
- package/dist/resource.d.ts +11 -0
- package/dist/resource.js +13 -0
- package/dist/view-field.d.ts +6 -0
- package/dist/view-field.js +9 -0
- package/package.json +1 -1
package/dist/access/resolve.js
CHANGED
|
@@ -124,7 +124,7 @@ const resolveAccess = async (source, ctx, options = {}) => {
|
|
|
124
124
|
if (await verdict(rules?.read, fieldContext("read"), true, `The read rule of field "${name}"`)) readableFields.push(name);
|
|
125
125
|
}
|
|
126
126
|
for (const operation of ["create", "update"]) {
|
|
127
|
-
if (!allowed[operation] || field.readOnly || !inList(lists[operation], name)) continue;
|
|
127
|
+
if (!allowed[operation] || field.readOnly || name === model.author || !inList(lists[operation], name)) continue;
|
|
128
128
|
if (await verdict(rules?.[operation], fieldContext(operation), true, `The ${operation} rule of field "${name}"`)) writable[operation].push(name);
|
|
129
129
|
}
|
|
130
130
|
}
|
|
@@ -4,7 +4,7 @@ import { ResolvedAccess } from "./resolve.js";
|
|
|
4
4
|
/**
|
|
5
5
|
* The resource as one user may see it: only readable fields remain, and a field is `readOnly`
|
|
6
6
|
* unless it is writable on create or update. `search` (and `searchMatch`) shrinks to readable fields, and `tenant` (with
|
|
7
|
-
* `tenantAcross`), `softDelete` and `
|
|
7
|
+
* `tenantAcross`), `softDelete`, `owner` and `author` are dropped when their field is hidden, `position` when its field or its `within` is, and each of `parents` whose field is. The event catalogue, whose conditions may use any
|
|
8
8
|
* field, is dropped. Key columns always stay,
|
|
9
9
|
* because records are addressed by them.
|
|
10
10
|
*
|
package/dist/access/restrict.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* The resource as one user may see it: only readable fields remain, and a field is `readOnly`
|
|
4
4
|
* unless it is writable on create or update. `search` (and `searchMatch`) shrinks to readable fields, and `tenant` (with
|
|
5
|
-
* `tenantAcross`), `softDelete` and `
|
|
5
|
+
* `tenantAcross`), `softDelete`, `owner` and `author` are dropped when their field is hidden, `position` when its field or its `within` is, and each of `parents` whose field is. The event catalogue, whose conditions may use any
|
|
6
6
|
* field, is dropped. Key columns always stay,
|
|
7
7
|
* because records are addressed by them.
|
|
8
8
|
*
|
|
@@ -17,10 +17,11 @@ const restrictModel = (model, access) => {
|
|
|
17
17
|
const keep = (name) => name !== void 0 && visible.has(name) ? name : void 0;
|
|
18
18
|
const search = model.search?.filter((name) => visible.has(name));
|
|
19
19
|
const searchMatch = model.searchMatch && Object.entries(model.searchMatch).filter(([name]) => visible.has(name));
|
|
20
|
-
const { search: _search, searchMatch: _searchMatch, tenant: _tenant, tenantAcross: _tenantAcross, softDelete: _softDelete, owner: _owner, position: _position, parents: _parents, events: _events, ...rest } = model;
|
|
20
|
+
const { search: _search, searchMatch: _searchMatch, tenant: _tenant, tenantAcross: _tenantAcross, softDelete: _softDelete, owner: _owner, author: _author, position: _position, parents: _parents, events: _events, ...rest } = model;
|
|
21
21
|
const tenant = keep(model.tenant);
|
|
22
22
|
const softDelete = keep(model.softDelete);
|
|
23
23
|
const owner = keep(model.owner);
|
|
24
|
+
const author = keep(model.author);
|
|
24
25
|
const position = model.position && keep(model.position.field) && (model.position.within === void 0 || keep(model.position.within)) ? model.position : void 0;
|
|
25
26
|
const parents = model.parents?.filter((parent) => visible.has(parent.field));
|
|
26
27
|
return {
|
|
@@ -35,6 +36,7 @@ const restrictModel = (model, access) => {
|
|
|
35
36
|
...tenant && model.tenantAcross && { tenantAcross: model.tenantAcross },
|
|
36
37
|
...softDelete && { softDelete },
|
|
37
38
|
...owner && { owner },
|
|
39
|
+
...author && { author },
|
|
38
40
|
...position && { position: { ...position } },
|
|
39
41
|
...parents?.length && { parents: parents.map((parent) => ({ ...parent })) }
|
|
40
42
|
};
|
package/dist/model.d.ts
CHANGED
|
@@ -128,6 +128,8 @@ export type ResourceModel = {
|
|
|
128
128
|
searchMatch?: Record<string, SearchMatch>;
|
|
129
129
|
/** The field holding the owning user's id; `.own` capability scopes filter on it. */
|
|
130
130
|
owner?: string;
|
|
131
|
+
/** The field holding the id of the user who created the record (`.author()`): creates set it, no request may. */
|
|
132
|
+
author?: string;
|
|
131
133
|
/** The integer field holding the records' order by hand (`.position()`), 1 the first. */
|
|
132
134
|
position?: {
|
|
133
135
|
/** The integer field, 1 the first record. */
|
|
@@ -299,6 +301,8 @@ export type FieldViewModel = {
|
|
|
299
301
|
helpWhen?: HelpWhen;
|
|
300
302
|
/** When the field shows on the record page: always, the default, only on its form or only on its read view. */
|
|
301
303
|
when?: ShowWhen;
|
|
304
|
+
/** The question a form or a list cell asks before the field's value first changes; cancelling keeps the value. */
|
|
305
|
+
confirm?: string;
|
|
302
306
|
/** How the value is shown. */
|
|
303
307
|
format?: FieldFormat;
|
|
304
308
|
/** A text that runs over several lines: forms edit it in a box that grows with it, and its line breaks show. */
|
package/dist/resource-model.d.ts
CHANGED
package/dist/resource-model.js
CHANGED
|
@@ -70,6 +70,7 @@ const assembleModel = (state) => {
|
|
|
70
70
|
...state.search && { search: [...state.search] },
|
|
71
71
|
...state.searchMatch && Object.keys(state.searchMatch).length > 0 && { searchMatch: { ...state.searchMatch } },
|
|
72
72
|
...state.owner && { owner: state.owner },
|
|
73
|
+
...state.author && { author: state.author },
|
|
73
74
|
...state.history && { history: true },
|
|
74
75
|
...state.comments && { comments: true },
|
|
75
76
|
...state.live && { live: true },
|
package/dist/resource.d.ts
CHANGED
|
@@ -82,6 +82,17 @@ export declare class ResourceBuilder<F extends FieldsInput = {}, K = never> {
|
|
|
82
82
|
search(pick: (r: SearchRefs<F>) => SearchRef[]): ResourceBuilder<F, K>;
|
|
83
83
|
/** The field holding the owning user's id, which `.own` capability scopes filter on. */
|
|
84
84
|
owner(pick: (r: FieldRefs<F>) => Ref): ResourceBuilder<F, K>;
|
|
85
|
+
/**
|
|
86
|
+
* The field holding the id of the user who created the record, such as a chat message's sender: every create sets it
|
|
87
|
+
* to the signed-in user's id, and no request may write it, so it is never the client's word. A create by no user
|
|
88
|
+
* leaves it empty.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* .author((r) => r.author_id)
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
author(pick: (r: FieldRefs<F>) => Ref): ResourceBuilder<F, K>;
|
|
85
96
|
/**
|
|
86
97
|
* The integer field holding the records' order, 1 the first, which a list's **Reorder** button changes by dragging the
|
|
87
98
|
* records; a move renumbers them 1, 2, 3, ... in one batch `reorder`. With `within`, a relation, each parent's records
|
package/dist/resource.js
CHANGED
|
@@ -84,6 +84,19 @@ var ResourceBuilder = class ResourceBuilder {
|
|
|
84
84
|
return this.next({ owner: pick(this.refs()).name });
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
|
+
* The field holding the id of the user who created the record, such as a chat message's sender: every create sets it
|
|
88
|
+
* to the signed-in user's id, and no request may write it, so it is never the client's word. A create by no user
|
|
89
|
+
* leaves it empty.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* .author((r) => r.author_id)
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
author(pick) {
|
|
97
|
+
return this.next({ author: pick(this.refs()).name });
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
87
100
|
* The integer field holding the records' order, 1 the first, which a list's **Reorder** button changes by dragging the
|
|
88
101
|
* records; a move renumbers them 1, 2, 3, ... in one batch `reorder`. With `within`, a relation, each parent's records
|
|
89
102
|
* have an order of their own, as an invoice's items do, sorted in the parent's related section. The field must be
|
package/dist/view-field.d.ts
CHANGED
|
@@ -26,6 +26,12 @@ export declare class FieldView<N extends string = string, V = unknown> {
|
|
|
26
26
|
* view and locked form; `always` when omitted. Its lists, cards and filters show it either way.
|
|
27
27
|
*/
|
|
28
28
|
when(when: ShowWhen): FieldView<N, V>;
|
|
29
|
+
/**
|
|
30
|
+
* Asks `question` before a person changes the value in a form or a list cell, such as a flag that is easy to set by
|
|
31
|
+
* mistake: once as the value first moves away from the saved one, not on every key. Cancelling keeps the value. See
|
|
32
|
+
* [Confirming a change](website/src/content/docs/reference/ui-config.mdx#confirming-a-change).
|
|
33
|
+
*/
|
|
34
|
+
confirm(question: string): FieldView<N, V>;
|
|
29
35
|
/**
|
|
30
36
|
* How the value shows: `badge` for statuses, `code` in monospace, `percent` with a % sign; for a text field, `email`,
|
|
31
37
|
* `phone` and `url` as a mail, call or open link, edited with an email, phone or address keyboard on a mobile screen.
|
package/dist/view-field.js
CHANGED
|
@@ -31,6 +31,15 @@ var FieldView = class FieldView {
|
|
|
31
31
|
return this.with({ when });
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
+
* Asks `question` before a person changes the value in a form or a list cell, such as a flag that is easy to set by
|
|
35
|
+
* mistake: once as the value first moves away from the saved one, not on every key. Cancelling keeps the value. See
|
|
36
|
+
* [Confirming a change](website/src/content/docs/reference/ui-config.mdx#confirming-a-change).
|
|
37
|
+
*/
|
|
38
|
+
confirm(question) {
|
|
39
|
+
if (question.trim() === "") throw new Error(`Field "${this.name}": .confirm() needs the question to ask`);
|
|
40
|
+
return this.with({ confirm: question });
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
34
43
|
* How the value shows: `badge` for statuses, `code` in monospace, `percent` with a % sign; for a text field, `email`,
|
|
35
44
|
* `phone` and `url` as a mail, call or open link, edited with an email, phone or address keyboard on a mobile screen.
|
|
36
45
|
*/
|