@adula/kit 1.0.0 → 1.2.0
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/build/agent/AGENTS.template.md +2 -2
- package/build/agent/capabilities.md +10 -5
- package/build/agent/skills/adula-frontend-design/SKILL.md +1 -1
- package/build/commands/doctor.js +37 -1
- package/build/commands/gaps.js +16 -6
- package/build/commands/install.js +19 -5
- package/build/commands/main.js +2 -0
- package/build/commands/module_add.js +2 -2
- package/build/commands/resource.js +1 -1
- package/build/commands/resource_snapshot.d.ts +15 -0
- package/build/commands/resource_snapshot.js +61 -0
- package/build/database/migrations/1770000000011_kit_managed_assignments.d.ts +5 -0
- package/build/database/migrations/1770000000011_kit_managed_assignments.js +10 -0
- package/build/database/migrations/1770000000012_kit_role_keys.d.ts +5 -0
- package/build/database/migrations/1770000000012_kit_role_keys.js +10 -0
- package/build/database/migrations/1770000000013_kit_notification_targets.d.ts +5 -0
- package/build/database/migrations/1770000000013_kit_notification_targets.js +10 -0
- package/build/database/migrations/1770000000014_kit_upload_grants.d.ts +5 -0
- package/build/database/migrations/1770000000014_kit_upload_grants.js +10 -0
- package/build/database/migrations/1770000000015_kit_inbound_webhooks.d.ts +5 -0
- package/build/database/migrations/1770000000015_kit_inbound_webhooks.js +10 -0
- package/build/index.d.ts +14 -5
- package/build/index.js +6 -2
- package/build/src/admin/contracts.d.ts +7 -0
- package/build/src/admin/contracts.js +15 -2
- package/build/src/admin/controller.d.ts +1 -0
- package/build/src/admin/controller.js +46 -1
- package/build/src/admin/presentation.d.ts +6 -0
- package/build/src/admin/record_title.d.ts +14 -0
- package/build/src/admin/record_title.js +50 -0
- package/build/src/admin/resource_service.d.ts +163 -2
- package/build/src/admin/resource_service.js +552 -43
- package/build/src/attachments/attachment_service.d.ts +12 -0
- package/build/src/attachments/attachment_service.js +28 -2
- package/build/src/attachments/upload_grants.d.ts +44 -0
- package/build/src/attachments/upload_grants.js +105 -0
- package/build/src/auth/ability.d.ts +1 -1
- package/build/src/auth/ability.js +4 -1
- package/build/src/auth/actor_store.js +6 -1
- package/build/src/auth/conditions.d.ts +15 -0
- package/build/src/auth/conditions.js +36 -0
- package/build/src/auth/sql.js +6 -2
- package/build/src/collaboration/assignments.d.ts +54 -3
- package/build/src/collaboration/assignments.js +125 -11
- package/build/src/collaboration/record_collaboration.js +5 -17
- package/build/src/commands/capabilities.js +22 -3
- package/build/src/commands/doctor.d.ts +31 -0
- package/build/src/commands/doctor.js +114 -0
- package/build/src/commands/gap_report.d.ts +50 -2
- package/build/src/commands/gap_report.js +102 -4
- package/build/src/commands/generator.js +3 -3
- package/build/src/commands/snapshot.d.ts +28 -0
- package/build/src/commands/snapshot.js +48 -0
- package/build/src/commands/source_markers.d.ts +10 -1
- package/build/src/commands/source_markers.js +36 -4
- package/build/src/core/administration_guard.js +3 -1
- package/build/src/core/message_templates.d.ts +4 -1
- package/build/src/core/message_templates.js +7 -2
- package/build/src/core/module_seed.d.ts +15 -0
- package/build/src/core/module_seed.js +31 -0
- package/build/src/core/notifications.d.ts +18 -1
- package/build/src/core/notifications.js +27 -1
- package/build/src/core/roles.d.ts +27 -1
- package/build/src/core/roles.js +133 -5
- package/build/src/database/schema.d.ts +27 -0
- package/build/src/database/schema.js +98 -0
- package/build/src/eslint/index.js +26 -0
- package/build/src/events/record_mutation.d.ts +4 -0
- package/build/src/events/record_mutation.js +2 -0
- package/build/src/integrations/imports.js +4 -2
- package/build/src/integrations/inbound_webhooks.d.ts +94 -0
- package/build/src/integrations/inbound_webhooks.js +276 -0
- package/build/src/integrations/openapi.js +69 -2
- package/build/src/integrations/print.js +1 -0
- package/build/src/resource/define_resource.js +12 -0
- package/build/src/resource/registry.d.ts +1 -0
- package/build/src/resource/registry.js +33 -0
- package/build/src/resource/types.d.ts +64 -1
- package/build/src/resource/values.js +2 -1
- package/build/src/services/settings.d.ts +13 -1
- package/build/src/services/settings.js +9 -2
- package/build/src/workflows/define_workflow.d.ts +30 -1
- package/build/src/workflows/define_workflow.js +44 -4
- package/build/src/workflows/engine.d.ts +24 -2
- package/build/src/workflows/engine.js +89 -24
- package/build/stubs/resource_contract.txt +103 -41
- package/package.json +1 -1
|
@@ -4,7 +4,13 @@ export function createResourceController(resolveRuntime, renderList, renderForm,
|
|
|
4
4
|
return class ResourcesController {
|
|
5
5
|
async create(ctx) {
|
|
6
6
|
return this.#execute(ctx, async ({ resources, actor }, resource) => {
|
|
7
|
-
|
|
7
|
+
// `?defaults[field]=value` pre-fills the form; the service authorizes each value.
|
|
8
|
+
const defaults = ctx.request.qs().defaults;
|
|
9
|
+
const editor = await resources.editor(resource.name, actor, undefined, {
|
|
10
|
+
defaults: defaults && typeof defaults === 'object' && !Array.isArray(defaults)
|
|
11
|
+
? defaults
|
|
12
|
+
: undefined,
|
|
13
|
+
});
|
|
8
14
|
return renderForm && ctx.request.accepts(['html', 'json']) === 'html'
|
|
9
15
|
? renderForm(ctx, resource, editor)
|
|
10
16
|
: editor;
|
|
@@ -50,8 +56,47 @@ export function createResourceController(resolveRuntime, renderList, renderForm,
|
|
|
50
56
|
: this.#positiveId(ctx.request.input('id')),
|
|
51
57
|
search: ctx.request.input('search'),
|
|
52
58
|
cursor: ctx.request.input('cursor'),
|
|
59
|
+
orgUnitId: ctx.request.input('orgUnitId') === undefined || ctx.request.input('orgUnitId') === ''
|
|
60
|
+
? undefined
|
|
61
|
+
: this.#positiveId(ctx.request.input('orgUnitId')),
|
|
62
|
+
purpose: ctx.request.input('purpose') === 'filter' ? 'filter' : 'form',
|
|
53
63
|
}));
|
|
54
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* GET …/aggregate?groupBy=status,priority&sum=total&filters[x]=…&where={json}
|
|
67
|
+
* Counts and totals over the records the actor may view (ResourceService.aggregate).
|
|
68
|
+
*/
|
|
69
|
+
async aggregate(ctx) {
|
|
70
|
+
return this.#execute(ctx, async ({ resources, actor }, resource) => {
|
|
71
|
+
const list = (value) => value === undefined || value === ''
|
|
72
|
+
? []
|
|
73
|
+
: Array.isArray(value)
|
|
74
|
+
? value.map(String)
|
|
75
|
+
: String(value).split(',').filter(Boolean);
|
|
76
|
+
let where;
|
|
77
|
+
const raw = ctx.request.input('where');
|
|
78
|
+
if (typeof raw === 'string' && raw) {
|
|
79
|
+
try {
|
|
80
|
+
where = JSON.parse(raw);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
throw new KitError(422, 'E_FILTER', 'where must be a JSON object');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else if (raw && typeof raw === 'object')
|
|
87
|
+
where = raw;
|
|
88
|
+
return {
|
|
89
|
+
data: await resources.aggregate(resource.name, actor, {
|
|
90
|
+
groupBy: list(ctx.request.input('groupBy')),
|
|
91
|
+
sum: list(ctx.request.input('sum')),
|
|
92
|
+
count: ctx.request.input('count') !== 'false',
|
|
93
|
+
filters: ctx.request.input('filters'),
|
|
94
|
+
where: where,
|
|
95
|
+
search: ctx.request.input('search'),
|
|
96
|
+
}),
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
}
|
|
55
100
|
async store(ctx) {
|
|
56
101
|
return this.#execute(ctx, async ({ resources, actor }, resource) => ctx.response.created({
|
|
57
102
|
data: await resources.save(resource.name, actor, ctx.request.body()),
|
|
@@ -6,6 +6,10 @@ export type RecordPermissions = Partial<Record<Action, boolean>>;
|
|
|
6
6
|
export type ResourceDescription = {
|
|
7
7
|
name: string;
|
|
8
8
|
label: string;
|
|
9
|
+
/** Singular record noun in Arabic, or null when the resource does not declare one. */
|
|
10
|
+
recordLabel: string | null;
|
|
11
|
+
/** Arabic create-button text, or null to derive «إضافة <recordLabel>» (or «إضافة سجل»). */
|
|
12
|
+
createLabel: string | null;
|
|
9
13
|
fields: ResourceField[];
|
|
10
14
|
list: string[];
|
|
11
15
|
show: string[];
|
|
@@ -19,4 +23,6 @@ export type ResourceNavigation = {
|
|
|
19
23
|
label: string;
|
|
20
24
|
href: string;
|
|
21
25
|
module: string;
|
|
26
|
+
/** Arabic module label, for grouping the navigation by module (1.2; optional for compatibility). */
|
|
27
|
+
moduleLabel?: string;
|
|
22
28
|
}[];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Resource, SerializedRecord } from '../resource/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The fields that name a record wherever it is referenced (#47, #32): the resource's
|
|
4
|
+
* `title`, or else its first sequence field and its first text field in `list`. Lookup
|
|
5
|
+
* keys are never shown raw; declared lookup fields are shown by their Arabic label.
|
|
6
|
+
*/
|
|
7
|
+
export declare function titleFields(resource: Pick<Resource, 'fields' | 'list' | 'title'>): string[];
|
|
8
|
+
/** Definition check: title fields exist, are readable and have a displayable type. */
|
|
9
|
+
export declare function assertTitle(resource: Pick<Resource, 'name' | 'fields' | 'list' | 'show' | 'serialize' | 'title'>): void;
|
|
10
|
+
/**
|
|
11
|
+
* The title of one serialized record, read after field-level filtering so it never shows
|
|
12
|
+
* a value the viewer may not read. Returns null when no title field has a value.
|
|
13
|
+
*/
|
|
14
|
+
export declare function formatTitle(resource: Pick<Resource, 'fields' | 'list' | 'title'>, record: SerializedRecord, lookupLabel: (group: string, key: string) => string | undefined): string | null;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const TITLE_TYPES = new Set(['string', 'text', 'integer', 'money', 'date', 'datetime', 'lookup']);
|
|
2
|
+
/**
|
|
3
|
+
* The fields that name a record wherever it is referenced (#47, #32): the resource's
|
|
4
|
+
* `title`, or else its first sequence field and its first text field in `list`. Lookup
|
|
5
|
+
* keys are never shown raw; declared lookup fields are shown by their Arabic label.
|
|
6
|
+
*/
|
|
7
|
+
export function titleFields(resource) {
|
|
8
|
+
if (resource.title)
|
|
9
|
+
return [...resource.title];
|
|
10
|
+
const sequence = Object.keys(resource.fields).find((key) => resource.fields[key].sequence);
|
|
11
|
+
const text = resource.list.find((key) => {
|
|
12
|
+
const field = resource.fields[key];
|
|
13
|
+
return (field.type === 'string' || field.type === 'text') && !field.sequence;
|
|
14
|
+
});
|
|
15
|
+
return [sequence, text].filter((key) => key !== undefined);
|
|
16
|
+
}
|
|
17
|
+
/** Definition check: title fields exist, are readable and have a displayable type. */
|
|
18
|
+
export function assertTitle(resource) {
|
|
19
|
+
if (!resource.title)
|
|
20
|
+
return;
|
|
21
|
+
if (!resource.title.length)
|
|
22
|
+
throw new Error(`${resource.name}: title needs at least one field`);
|
|
23
|
+
const readable = new Set(resource.serialize ?? [...resource.list, ...resource.show]);
|
|
24
|
+
for (const key of resource.title) {
|
|
25
|
+
const field = resource.fields[key];
|
|
26
|
+
if (!field)
|
|
27
|
+
throw new Error(`Unknown field: ${key}`);
|
|
28
|
+
if (!TITLE_TYPES.has(field.type))
|
|
29
|
+
throw new Error(`${resource.name}: title field ${key} cannot be a ${field.type}`);
|
|
30
|
+
if (!readable.has(key))
|
|
31
|
+
throw new Error(`${resource.name}: title field ${key} must be serialized`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The title of one serialized record, read after field-level filtering so it never shows
|
|
36
|
+
* a value the viewer may not read. Returns null when no title field has a value.
|
|
37
|
+
*/
|
|
38
|
+
export function formatTitle(resource, record, lookupLabel) {
|
|
39
|
+
const parts = [];
|
|
40
|
+
for (const key of titleFields(resource)) {
|
|
41
|
+
const value = record[key];
|
|
42
|
+
if (value === null || value === undefined || value === '')
|
|
43
|
+
continue;
|
|
44
|
+
const field = resource.fields[key];
|
|
45
|
+
parts.push(field.type === 'lookup'
|
|
46
|
+
? (lookupLabel(field.group, String(value)) ?? String(value))
|
|
47
|
+
: String(value));
|
|
48
|
+
}
|
|
49
|
+
return parts.length ? parts.join(' · ') : null;
|
|
50
|
+
}
|
|
@@ -1,8 +1,27 @@
|
|
|
1
1
|
import type { Knex } from 'knex';
|
|
2
2
|
import type { ResourceRegistry } from '../resource/registry.js';
|
|
3
|
-
import type { Action, RecordData, Resource, SerializedRecord } from '../resource/types.js';
|
|
3
|
+
import type { Action, JsonValue, RecordData, Resource, SerializedRecord } from '../resource/types.js';
|
|
4
4
|
import { type Actor, type KitAbility } from '../auth/ability.js';
|
|
5
|
+
import { type Conditions } from '../auth/conditions.js';
|
|
5
6
|
import type { ResourceDescription, ResourceNavigation, ResourceField } from './presentation.js';
|
|
7
|
+
export type AggregateOptions = {
|
|
8
|
+
/** Up to three queryable fields (docStatus and orgUnitId are also accepted). */
|
|
9
|
+
groupBy?: string[];
|
|
10
|
+
/** Count rows per group (default true). */
|
|
11
|
+
count?: boolean;
|
|
12
|
+
/** Integer or money fields to total per group; money totals are decimal strings. */
|
|
13
|
+
sum?: string[];
|
|
14
|
+
/** Equality filters, as in list(). */
|
|
15
|
+
filters?: Record<string, unknown>;
|
|
16
|
+
/** Role-rule style conditions ($eq, $ne, $in, $lt, $gt, $like, "$actor.id"). */
|
|
17
|
+
where?: Conditions;
|
|
18
|
+
search?: string;
|
|
19
|
+
};
|
|
20
|
+
export type AggregateRow = {
|
|
21
|
+
group: Record<string, JsonValue>;
|
|
22
|
+
count?: number;
|
|
23
|
+
sum?: Record<string, string | null>;
|
|
24
|
+
};
|
|
6
25
|
export type ListOptions = {
|
|
7
26
|
limit?: number;
|
|
8
27
|
cursor?: string;
|
|
@@ -19,6 +38,24 @@ export type ListOptions = {
|
|
|
19
38
|
* indirectly, so it needs unconditional view access at the field's level.
|
|
20
39
|
*/
|
|
21
40
|
export declare function canQueryField(resource: Resource, actor: Actor, ability: KitAbility, key: string): boolean;
|
|
41
|
+
/** Options of ResourceService.systemSave. */
|
|
42
|
+
export type SystemSaveOptions = {
|
|
43
|
+
/** The user recorded as the author: created/updated by, activity and events. */
|
|
44
|
+
actorId: number;
|
|
45
|
+
/** Write inside the module's transaction. */
|
|
46
|
+
trx?: Knex.Transaction;
|
|
47
|
+
/** Why module code wrote the record; stored with the activity entry. */
|
|
48
|
+
reason?: string;
|
|
49
|
+
/** Optimistic lock: when given, it must match the stored version. */
|
|
50
|
+
version?: number;
|
|
51
|
+
/**
|
|
52
|
+
* The person who chose the values, when module code writes a choice made by a user,
|
|
53
|
+
* such as a supervisor reassigning a record. A new value of a user field must then be
|
|
54
|
+
* eligible for them as in a user save; without it, any active member qualifies on
|
|
55
|
+
* unscoped resources.
|
|
56
|
+
*/
|
|
57
|
+
chooser?: Actor;
|
|
58
|
+
};
|
|
22
59
|
export declare class ResourceService {
|
|
23
60
|
private db;
|
|
24
61
|
private registry;
|
|
@@ -37,6 +74,8 @@ export declare class ResourceService {
|
|
|
37
74
|
private scopedQuery;
|
|
38
75
|
private columns;
|
|
39
76
|
private find;
|
|
77
|
+
/** A live record regardless of any actor's scope, for system writes. */
|
|
78
|
+
private findAny;
|
|
40
79
|
private requireRecord;
|
|
41
80
|
/**
|
|
42
81
|
* Authorizes one record for collaboration features (comments, tags, assignments...).
|
|
@@ -62,7 +101,24 @@ export declare class ResourceService {
|
|
|
62
101
|
related: Record<string, SerializedRecord[]>;
|
|
63
102
|
}>;
|
|
64
103
|
private canQueryField;
|
|
104
|
+
/**
|
|
105
|
+
* Counts and totals over the records the actor may view, grouped by queryable fields,
|
|
106
|
+
* for dashboards. Authorization is list()'s: the same rules, organization scope and
|
|
107
|
+
* soft-delete filter apply, and every grouped, totalled or filtered field must be one
|
|
108
|
+
* the actor may query (E_FIELD_FORBIDDEN otherwise), so no hidden value leaks.
|
|
109
|
+
*/
|
|
110
|
+
aggregate(name: string, actor: Actor, options?: AggregateOptions): Promise<{
|
|
111
|
+
rows: AggregateRow[];
|
|
112
|
+
truncated: boolean;
|
|
113
|
+
}>;
|
|
65
114
|
private preload;
|
|
115
|
+
/** Formats record titles of one resource, loading the labels of its title lookups once. */
|
|
116
|
+
private titler;
|
|
117
|
+
/**
|
|
118
|
+
* Titles of records the actor may view, read under the actor's field access (#32).
|
|
119
|
+
* Records the actor cannot view are left out; callers fall back to the record id.
|
|
120
|
+
*/
|
|
121
|
+
titles(name: string, ids: readonly number[], actor: Actor): Promise<Map<number, string>>;
|
|
66
122
|
show(name: string, id: number, actor: Actor): Promise<{
|
|
67
123
|
data: SerializedRecord;
|
|
68
124
|
permissions: Partial<Record<Action, boolean>>;
|
|
@@ -72,6 +128,10 @@ export declare class ResourceService {
|
|
|
72
128
|
id?: number;
|
|
73
129
|
search?: string;
|
|
74
130
|
cursor?: string;
|
|
131
|
+
/** User fields: the organization unit chosen in the form. */
|
|
132
|
+
orgUnitId?: number;
|
|
133
|
+
/** User fields: choices for a list filter instead of a form. */
|
|
134
|
+
purpose?: 'form' | 'filter';
|
|
75
135
|
}): Promise<{
|
|
76
136
|
data: {
|
|
77
137
|
value: string;
|
|
@@ -79,11 +139,25 @@ export declare class ResourceService {
|
|
|
79
139
|
}[];
|
|
80
140
|
nextCursor: string | null;
|
|
81
141
|
}>;
|
|
142
|
+
/**
|
|
143
|
+
* Choices for a user field. A form lists active members of the record's unit or its
|
|
144
|
+
* ancestors (the users the record is visible to through membership); a filter lists
|
|
145
|
+
* active users who share organization scope with the actor. Only names are returned.
|
|
146
|
+
*/
|
|
147
|
+
private userOptions;
|
|
148
|
+
/**
|
|
149
|
+
* Active users eligible for a user field: members of the unit at `path` or of one of
|
|
150
|
+
* its ancestors, or (without a path) users sharing organization scope with the actor.
|
|
151
|
+
* A system write on an unscoped resource passes no actor: any active member qualifies.
|
|
152
|
+
*/
|
|
153
|
+
private eligibleUsers;
|
|
82
154
|
/** Deferred relation reads re-authorize the parent and each child on every request. */
|
|
83
155
|
children(name: string, id: number, actor: Actor): Promise<Record<string, {
|
|
84
156
|
rows: SerializedRecord[];
|
|
85
157
|
hasMore: boolean;
|
|
86
158
|
}>>;
|
|
159
|
+
/** Inline children of an already authorized parent record. */
|
|
160
|
+
private childrenOf;
|
|
87
161
|
/** Active lookup labels for the lookup fields the actor may read on this resource. */
|
|
88
162
|
lookups(name: string, actor: Actor): Promise<Record<string, {
|
|
89
163
|
value: string;
|
|
@@ -103,10 +177,66 @@ export declare class ResourceService {
|
|
|
103
177
|
actorEmail: string | null;
|
|
104
178
|
createdAt: string;
|
|
105
179
|
}[]>;
|
|
106
|
-
|
|
180
|
+
/**
|
|
181
|
+
* Inline children and recent activity of one record, authorized with a single record
|
|
182
|
+
* read (for deferred page props that would otherwise read the record twice).
|
|
183
|
+
*/
|
|
184
|
+
details(name: string, id: number, actor: Actor): Promise<{
|
|
185
|
+
children: Record<string, {
|
|
186
|
+
rows: SerializedRecord[];
|
|
187
|
+
hasMore: boolean;
|
|
188
|
+
}>;
|
|
189
|
+
activity: {
|
|
190
|
+
id: number;
|
|
191
|
+
action: Action;
|
|
192
|
+
fields: string[];
|
|
193
|
+
actorId: number;
|
|
194
|
+
actorName: string | null;
|
|
195
|
+
/**
|
|
196
|
+
* @deprecated Always null. Kept so project-owned copies of resource-show from
|
|
197
|
+
* earlier releases still compile; they fall back to the actor id.
|
|
198
|
+
*/
|
|
199
|
+
actorEmail: string | null;
|
|
200
|
+
createdAt: string;
|
|
201
|
+
}[];
|
|
202
|
+
}>;
|
|
203
|
+
/** show(), children() and activity() of one record from a single record read. */
|
|
204
|
+
record(name: string, id: number, actor: Actor): Promise<{
|
|
205
|
+
data: SerializedRecord;
|
|
206
|
+
permissions: Partial<Record<Action, boolean>>;
|
|
207
|
+
related: Record<string, SerializedRecord[]>;
|
|
208
|
+
children: Record<string, {
|
|
209
|
+
rows: SerializedRecord[];
|
|
210
|
+
hasMore: boolean;
|
|
211
|
+
}>;
|
|
212
|
+
activity: {
|
|
213
|
+
id: number;
|
|
214
|
+
action: Action;
|
|
215
|
+
fields: string[];
|
|
216
|
+
actorId: number;
|
|
217
|
+
actorName: string | null;
|
|
218
|
+
/**
|
|
219
|
+
* @deprecated Always null. Kept so project-owned copies of resource-show from
|
|
220
|
+
* earlier releases still compile; they fall back to the actor id.
|
|
221
|
+
*/
|
|
222
|
+
actorEmail: string | null;
|
|
223
|
+
createdAt: string;
|
|
224
|
+
}[];
|
|
225
|
+
}>;
|
|
226
|
+
private activityOf;
|
|
227
|
+
/**
|
|
228
|
+
* The form description for create (no id) or update. `defaults` pre-fills a create form
|
|
229
|
+
* (#48), for example `?defaults[violation]=13`: only visible form fields are used, a
|
|
230
|
+
* related record must be viewable by the actor and a lookup must be active. Anything else
|
|
231
|
+
* is dropped. Defaults are only initial values; saving validates as usual.
|
|
232
|
+
*/
|
|
233
|
+
editor(name: string, actor: Actor, id?: number, request?: {
|
|
234
|
+
defaults?: Record<string, unknown>;
|
|
235
|
+
}): Promise<{
|
|
107
236
|
mode: string;
|
|
108
237
|
name: string;
|
|
109
238
|
label: string;
|
|
239
|
+
recordLabel: string | null;
|
|
110
240
|
fields: ({
|
|
111
241
|
label: import("../resource/types.js").Label;
|
|
112
242
|
column?: string;
|
|
@@ -146,6 +276,18 @@ export declare class ResourceService {
|
|
|
146
276
|
type: "belongsTo";
|
|
147
277
|
resource: string;
|
|
148
278
|
key: string;
|
|
279
|
+
} | {
|
|
280
|
+
label: import("../resource/types.js").Label;
|
|
281
|
+
column?: string;
|
|
282
|
+
required?: boolean;
|
|
283
|
+
unique?: boolean;
|
|
284
|
+
sortable?: boolean;
|
|
285
|
+
searchable?: boolean;
|
|
286
|
+
filterable?: boolean;
|
|
287
|
+
permissionLevel?: number;
|
|
288
|
+
sequence?: string;
|
|
289
|
+
type: "user";
|
|
290
|
+
key: string;
|
|
149
291
|
} | {
|
|
150
292
|
label: import("../resource/types.js").Label;
|
|
151
293
|
column?: string;
|
|
@@ -175,6 +317,8 @@ export declare class ResourceService {
|
|
|
175
317
|
group: string;
|
|
176
318
|
key: string;
|
|
177
319
|
})[];
|
|
320
|
+
/** Initial values of a create form, already checked against the actor's access. */
|
|
321
|
+
defaults: SerializedRecord;
|
|
178
322
|
inline: Record<string, {
|
|
179
323
|
rows: SerializedRecord[];
|
|
180
324
|
hasMore: boolean;
|
|
@@ -197,9 +341,26 @@ export declare class ResourceService {
|
|
|
197
341
|
label: string;
|
|
198
342
|
}[];
|
|
199
343
|
scoped: boolean;
|
|
344
|
+
/** The belongsTo field whose record decides the unit; the form shows no unit picker. */
|
|
345
|
+
scopeFrom: string | null;
|
|
200
346
|
record: SerializedRecord | null;
|
|
201
347
|
}>;
|
|
202
348
|
save(name: string, actor: Actor, input: RecordData, id?: number, transaction?: Knex.Transaction): Promise<SerializedRecord>;
|
|
349
|
+
/**
|
|
350
|
+
* A write decided by module code rather than by a user's role rules: state transitions,
|
|
351
|
+
* snapshots and listener updates. It runs the validator, hooks, lookup, relation and
|
|
352
|
+
* attachment checks, versioning and the audit trail exactly like save(), and skips only
|
|
353
|
+
* the actor's role rules and organization scope. Values may set any stored field except
|
|
354
|
+
* sequences and inline children. Updates merge the given values into the stored record.
|
|
355
|
+
* Returns the full stored record; module code must not send it to users unfiltered.
|
|
356
|
+
*/
|
|
357
|
+
systemSave(name: string, values: RecordData, id: number | undefined, options: SystemSaveOptions): Promise<RecordData>;
|
|
358
|
+
/**
|
|
359
|
+
* Moves the records whose scope follows this parent (`scope: { from }`) to the parent's
|
|
360
|
+
* current organization unit, through systemSave. Call it from a listener on the parent's
|
|
361
|
+
* `updated` event, or right after moving the parent. Returns the number of moved records.
|
|
362
|
+
*/
|
|
363
|
+
rehome(parentName: string, parentId: number, options: Omit<SystemSaveOptions, 'version'>): Promise<number>;
|
|
203
364
|
private persist;
|
|
204
365
|
private requireVersion;
|
|
205
366
|
/** Inline children share the parent's draft state, organization and update authority. */
|