@protobase/server 0.68.0 → 0.68.2
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/check-views.js +6 -0
- package/dist/comments/record-comments.js +1 -0
- package/dist/history/activity.js +10 -0
- package/dist/jobs/failed-jobs.js +3 -0
- package/dist/package.js +1 -1
- package/dist/user-tables/admin-user-tables.js +9 -4
- package/dist/user-tables/definitions-views.js +93 -0
- package/dist/view-access.js +1 -0
- package/package.json +6 -6
package/dist/check-views.js
CHANGED
|
@@ -69,6 +69,11 @@ const textHintErrors = (view, model) => Object.entries(view.fields).flatMap(([na
|
|
|
69
69
|
const format = hints.format !== void 0 && textFormats.has(hints.format) ? [`format('${hints.format}') of "${name}" needs a text field, not ${field.type}`] : [];
|
|
70
70
|
return [...textHints.filter((hint) => hints[hint]).map((hint) => `${hint} "${name}" needs a text field, not ${field.type}`), ...format];
|
|
71
71
|
});
|
|
72
|
+
const notOnCreateErrors = (view, model) => Object.entries(view.fields).flatMap(([name, hints]) => {
|
|
73
|
+
const field = model.fields[name];
|
|
74
|
+
if (!hints.notOnCreate || !field || field.nullable || field.default || field.readOnly) return [];
|
|
75
|
+
return [`notOnCreate "${name}" is a field a create needs: make it optional or give it a default`];
|
|
76
|
+
});
|
|
72
77
|
const linkErrors = (view, model, models, pages, createPages) => view.actions.flatMap(({ name, label, run }) => {
|
|
73
78
|
if (run?.kind !== "link") return [];
|
|
74
79
|
const error = linkActionError(label, run);
|
|
@@ -192,6 +197,7 @@ const checkViews = (registry, views, pages = [], fieldTypes = {}) => {
|
|
|
192
197
|
...valueLabelErrors(view, model),
|
|
193
198
|
...valueColorErrors(view, model),
|
|
194
199
|
...textHintErrors(view, model),
|
|
200
|
+
...notOnCreateErrors(view, model),
|
|
195
201
|
...numberHintErrors(view, model),
|
|
196
202
|
...listEditErrors(view, model),
|
|
197
203
|
...widgetErrors(view, model, fieldTypes),
|
|
@@ -29,6 +29,7 @@ const commentsView = view("record_comments").names({
|
|
|
29
29
|
singular: "Comment",
|
|
30
30
|
plural: "Comments"
|
|
31
31
|
}).icon("message-square").title((r) => r.record_key).help("Every comment on the records whose resource takes comments, newest first. Delete one to take it down.").fields((r) => ({
|
|
32
|
+
resource: r.resource.label("Resource"),
|
|
32
33
|
record_key: r.record_key.label("Record"),
|
|
33
34
|
author_id: r.author_id.label("By"),
|
|
34
35
|
body: r.body.label("Comment"),
|
package/dist/history/activity.js
CHANGED
|
@@ -39,11 +39,21 @@ const activityView = view("record_history").names({
|
|
|
39
39
|
singular: "Change",
|
|
40
40
|
plural: "Activity"
|
|
41
41
|
}).icon("history").title((r) => r.record_key).help("Every change of the records whose resource keeps history, from the app or straight in the database, newest first.").fields((r) => ({
|
|
42
|
+
resource: r.resource.label("Resource"),
|
|
42
43
|
record_key: r.record_key.label("Record"),
|
|
44
|
+
operation: r.operation.label("Operation").valueLabels({
|
|
45
|
+
create: "Created",
|
|
46
|
+
update: "Updated",
|
|
47
|
+
delete: "Deleted",
|
|
48
|
+
undelete: "Restored"
|
|
49
|
+
}),
|
|
50
|
+
before: r.before.label("Before"),
|
|
51
|
+
after: r.after.label("After"),
|
|
43
52
|
actor_id: r.actor_id.label("By"),
|
|
44
53
|
actor_roles: r.actor_roles.label("Acting as"),
|
|
45
54
|
database_role: r.database_role.label("Database role"),
|
|
46
55
|
changed: r.changed.label("Changed fields"),
|
|
56
|
+
transaction: r.transaction.label("Transaction"),
|
|
47
57
|
at: r.at.label("When")
|
|
48
58
|
})).list((r) => ({
|
|
49
59
|
columns: [
|
package/dist/jobs/failed-jobs.js
CHANGED
|
@@ -30,8 +30,11 @@ const failedJobsView = view("failed_jobs").names({
|
|
|
30
30
|
}).icon("triangle-alert").title((r) => r.name).help("Jobs that used up their attempts, or failed for good. Retry one once its cause is fixed: it gets one more attempt.").fields((r) => ({
|
|
31
31
|
name: r.name.label("Job"),
|
|
32
32
|
error: r.error.label("Last error"),
|
|
33
|
+
attempts: r.attempts.label("Attempts"),
|
|
33
34
|
failed_at: r.failed_at.label("Failed"),
|
|
34
35
|
created_at: r.created_at.label("Enqueued"),
|
|
36
|
+
key: r.key.label("Key"),
|
|
37
|
+
payload: r.payload.label("Payload"),
|
|
35
38
|
output: r.output.label("Error details")
|
|
36
39
|
})).list((r) => ({
|
|
37
40
|
columns: [
|
package/dist/package.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { definitionsResources, fieldsResourceName, tablesResourceName } from "./definitions-resources.js";
|
|
2
2
|
import { definitionsHook } from "./definitions-hook.js";
|
|
3
|
+
import { fieldsView, tablesView } from "./definitions-views.js";
|
|
3
4
|
import { servedUserTables } from "./served-user-tables.js";
|
|
4
5
|
//#region src/user-tables/admin-user-tables.ts
|
|
5
6
|
/**
|
|
6
7
|
* What the tables users add bring to the admin, with `userTables` on: the resources over their definitions, through
|
|
7
|
-
* which they are created, each user table a name in code (`codeNames`, the
|
|
8
|
-
* hook that creates their tables and columns. Without `tables`, nothing.
|
|
9
|
-
* to the organization it was made in (`definitionsResources`).
|
|
8
|
+
* which they are created, with the views the admin app makes them in, each user table a name in code (`codeNames`, the
|
|
9
|
+
* app's and Protobase's) does not take, and the hook that creates their tables and columns. Without `tables`, nothing.
|
|
10
|
+
* With `organizations`, each definition belongs to the organization it was made in (`definitionsResources`).
|
|
10
11
|
*/
|
|
11
12
|
const adminUserTables = (tables, codeNames, roles, organizations = false) => {
|
|
12
13
|
if (!tables) return {
|
|
@@ -31,7 +32,11 @@ const adminUserTables = (tables, codeNames, roles, organizations = false) => {
|
|
|
31
32
|
definitions.fields,
|
|
32
33
|
...served.resources
|
|
33
34
|
],
|
|
34
|
-
views:
|
|
35
|
+
views: [
|
|
36
|
+
tablesView,
|
|
37
|
+
fieldsView,
|
|
38
|
+
...served.views
|
|
39
|
+
],
|
|
35
40
|
hooks: [definitionsHook(codeNames)],
|
|
36
41
|
warnings: served.warnings
|
|
37
42
|
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { fieldsResourceName, tablesResourceName } from "./definitions-resources.js";
|
|
2
|
+
import { l, view } from "@protobase/schema";
|
|
3
|
+
//#region src/user-tables/definitions-views.ts
|
|
4
|
+
/** What each field type is called in the admin app; the UI's locale files translate them. */
|
|
5
|
+
const fieldTypeLabels = {
|
|
6
|
+
text: "Text",
|
|
7
|
+
long_text: "Long text",
|
|
8
|
+
integer: "Whole number",
|
|
9
|
+
decimal: "Decimal number",
|
|
10
|
+
boolean: "Yes or no",
|
|
11
|
+
date: "Date",
|
|
12
|
+
timestamp: "Date and time",
|
|
13
|
+
json: "JSON"
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The tables list of the admin app, for whoever may make tables: a table's page has its fields, a link to add one and a
|
|
17
|
+
* link to the table's own list, which shows once the app serves it. The title field is set once its field exists.
|
|
18
|
+
*/
|
|
19
|
+
const tablesView = view(tablesResourceName).names({
|
|
20
|
+
singular: "Table",
|
|
21
|
+
plural: "Tables"
|
|
22
|
+
}).title((r) => r.plural_label).help("The tables people add to the app. Open one to give it fields or to go to its records.").nav({
|
|
23
|
+
creatorsOnly: true,
|
|
24
|
+
order: 0
|
|
25
|
+
}).fields((r) => ({
|
|
26
|
+
name: r.name.label("Name").format("code").help("Its address and the name of its database table: lowercase letters, digits and _, starting with a letter. It cannot change later."),
|
|
27
|
+
plural_label: r.plural_label.label("Plural label").help("Its name in the sidebar and above its list, such as Notes."),
|
|
28
|
+
singular_label: r.singular_label.label("Singular label").help("What one record is called, such as Note."),
|
|
29
|
+
title_field: r.title_field.label("Title field").notOnCreate().help("The name of the text field that titles each record and that search looks in.")
|
|
30
|
+
})).list((r) => ({
|
|
31
|
+
columns: [r.plural_label, r.name],
|
|
32
|
+
sort: [[r.name, "asc"]]
|
|
33
|
+
})).layout((r) => [l.section("Table", [
|
|
34
|
+
r.plural_label,
|
|
35
|
+
r.singular_label,
|
|
36
|
+
r.name,
|
|
37
|
+
r.title_field
|
|
38
|
+
]), l.related("Fields", {
|
|
39
|
+
resource: fieldsResourceName,
|
|
40
|
+
field: "table_id",
|
|
41
|
+
columns: [
|
|
42
|
+
"label",
|
|
43
|
+
"name",
|
|
44
|
+
"type"
|
|
45
|
+
],
|
|
46
|
+
sort: "position",
|
|
47
|
+
empty: "No fields yet."
|
|
48
|
+
})]).actions((a) => [a.link("open_table", {
|
|
49
|
+
label: "Open table",
|
|
50
|
+
icon: "table",
|
|
51
|
+
to: {
|
|
52
|
+
kind: "list",
|
|
53
|
+
resource: "{table_name}"
|
|
54
|
+
}
|
|
55
|
+
}), a.link("add_field", {
|
|
56
|
+
label: "Add field",
|
|
57
|
+
icon: "plus",
|
|
58
|
+
to: {
|
|
59
|
+
kind: "create",
|
|
60
|
+
resource: fieldsResourceName,
|
|
61
|
+
values: { table_id: "{id}" }
|
|
62
|
+
}
|
|
63
|
+
})]);
|
|
64
|
+
/** A table's fields, reached from its page; a field users add is always optional, so its form leaves that out. */
|
|
65
|
+
const fieldsView = view(fieldsResourceName).names({
|
|
66
|
+
singular: "Field",
|
|
67
|
+
plural: "Fields"
|
|
68
|
+
}).title((r) => r.label).nav({ hidden: true }).fields((r) => ({
|
|
69
|
+
table_id: r.table_id.label("Table"),
|
|
70
|
+
label: r.label.label("Label").help("What the field is called on screen."),
|
|
71
|
+
name: r.name.label("Name").format("code").help("Its name in the API and its database column: lowercase letters, digits and _, starting with a letter. It cannot change later."),
|
|
72
|
+
type: r.type.label("Type").valueLabels(fieldTypeLabels).help("What it holds. It cannot change later."),
|
|
73
|
+
position: r.position.label("Position").help("Where it sits among the fields of its table, 1 the first."),
|
|
74
|
+
precision: r.precision.label("Digits").help("For a decimal number only: how many digits it holds in all, from 1 to 1000."),
|
|
75
|
+
scale: r.scale.label("Decimals").help("For a decimal number only: how many of those digits come after the decimal point."),
|
|
76
|
+
required: r.required.notOnCreate()
|
|
77
|
+
})).list((r) => ({
|
|
78
|
+
columns: [
|
|
79
|
+
r.label,
|
|
80
|
+
r.name,
|
|
81
|
+
r.type,
|
|
82
|
+
r.position
|
|
83
|
+
],
|
|
84
|
+
sort: [[r.position, "asc"]]
|
|
85
|
+
})).layout((r) => [l.section("Field", [
|
|
86
|
+
r.table_id,
|
|
87
|
+
r.label,
|
|
88
|
+
r.name,
|
|
89
|
+
r.type,
|
|
90
|
+
r.position
|
|
91
|
+
]), l.section("Decimal number", [r.precision, r.scale])]);
|
|
92
|
+
//#endregion
|
|
93
|
+
export { fieldTypeLabels, fieldsView, tablesView };
|
package/dist/view-access.js
CHANGED
|
@@ -306,6 +306,7 @@ const actionShape = (model, models, createPages) => shapes.object({
|
|
|
306
306
|
const viewShape = (resource, model, models, prompted, createPages, writeOnly) => {
|
|
307
307
|
const nav = shapes.object({
|
|
308
308
|
hidden: shapes.data,
|
|
309
|
+
creatorsOnly: shapes.data,
|
|
309
310
|
group: shapes.data,
|
|
310
311
|
order: shapes.data,
|
|
311
312
|
tenant: shapes.data,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/server",
|
|
3
|
-
"version": "0.68.
|
|
3
|
+
"version": "0.68.2",
|
|
4
4
|
"description": "The Protobase REST API as a Hono app, with Better Auth sign-in.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -44,11 +44,11 @@
|
|
|
44
44
|
"@better-auth/sso": "^1.7.12",
|
|
45
45
|
"@livekit/protocol": "1.53.1",
|
|
46
46
|
"@modelcontextprotocol/server": "^2.3.1",
|
|
47
|
-
"@protobase/layout": "0.68.
|
|
48
|
-
"@protobase/query": "0.68.
|
|
49
|
-
"@protobase/schema": "0.68.
|
|
50
|
-
"@protobase/workflow-plugins": "0.68.
|
|
51
|
-
"@protobase/workflows": "0.68.
|
|
47
|
+
"@protobase/layout": "0.68.2",
|
|
48
|
+
"@protobase/query": "0.68.2",
|
|
49
|
+
"@protobase/schema": "0.68.2",
|
|
50
|
+
"@protobase/workflow-plugins": "0.68.2",
|
|
51
|
+
"@protobase/workflows": "0.68.2",
|
|
52
52
|
"@react-email/render": "^2.1.0",
|
|
53
53
|
"@react-pdf/primitives": "^4.4.0",
|
|
54
54
|
"@react-pdf/renderer": "^4.9.0",
|