@protobase/server 0.19.12 → 0.19.13
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 +8 -2
- package/dist/view-access.js +12 -6
- package/dist/view-related-column.js +25 -0
- package/package.json +4 -4
package/dist/check-views.js
CHANGED
|
@@ -3,6 +3,7 @@ import { cardPhotosErrors } from "./view-card-photos.js";
|
|
|
3
3
|
import { cardFavoriteErrors, favoriteOwnerErrors } from "./view-card-favorite.js";
|
|
4
4
|
import { saveSearchErrors, searchesOfErrors } from "./view-saved-search.js";
|
|
5
5
|
import { mapDisplayErrors, mapErrors } from "./view-map.js";
|
|
6
|
+
import { relatedColumnErrors } from "./view-related-column.js";
|
|
6
7
|
import { addressErrors, addressPlaceholders, displayFields, fieldRefsIn, hrefField, hrefPlaceholders, linkActionError } from "@protobase/schema/internal";
|
|
7
8
|
//#region src/check-views.ts
|
|
8
9
|
const linkFields = (run) => run?.kind !== "link" ? [] : "to" in run ? addressPlaceholders(run.to) : hrefPlaceholders(run.href);
|
|
@@ -79,6 +80,11 @@ const sharedPhotosErrors = (view) => {
|
|
|
79
80
|
const same = (a, b) => a.resource === b.resource && a.field === b.field && a.image === b.image && a.sort === b.sort;
|
|
80
81
|
return shown.filter(({ photos }) => !same(photos, shown[0].photos)).map(({ name }) => `display "${name}" photos: the list's records load with one set of photos, so name those of display "${shown[0].name}"`);
|
|
81
82
|
};
|
|
83
|
+
const relatedColumnsErrors = (view, model, models) => (view.list?.relatedColumns ?? []).flatMap((column) => [
|
|
84
|
+
...Object.hasOwn(model.fields, column.name) ? [`"${column.name}" is a field of ${model.name}`] : [],
|
|
85
|
+
...view.list.columns.includes(column.name) ? [] : ["it is not among the list's columns"],
|
|
86
|
+
...relatedColumnErrors(column, view.resource, models)
|
|
87
|
+
].map((error) => `related column "${column.name}": ${error}`));
|
|
82
88
|
const galleryErrors = (item, resource, model, models) => {
|
|
83
89
|
const image = item.image === void 0 ? void 0 : model.fields[item.image];
|
|
84
90
|
return [...image && image.type !== "file" ? [`"${item.image}" is not a file field`] : [], ...item.photos ? cardPhotosErrors(item.photos, resource, models).map((error) => `photos: ${error}`) : []];
|
|
@@ -92,7 +98,7 @@ const listOfErrors = (model, name, models) => {
|
|
|
92
98
|
* Fails at startup when a view uses a sensitive field where it would show empty, a display or custom section names a
|
|
93
99
|
* field the resource does not have, labels or colours values its enum does not have, shows a field with a field type of
|
|
94
100
|
* `fieldTypes` (each with the storage types it shows) that does not show it,
|
|
95
|
-
* has a related section, a cards display's or gallery's photos or a cards display's favorite button, a `listOf`, a "Save search" button or a
|
|
101
|
+
* has a related section or related list column, a cards display's or gallery's photos or a cards display's favorite button, a `listOf`, a "Save search" button or a
|
|
96
102
|
* `searchesOf` that do not fit the resources, cards
|
|
97
103
|
* displays of one list that name different photos, a map that cannot show the
|
|
98
104
|
* record's position or a map display that cannot find the records in its view, a link to an address the app does not have, or a create page that is not one of `pages`.
|
|
@@ -103,7 +109,7 @@ const checkViews = (registry, views, pages = [], fieldTypes = {}) => {
|
|
|
103
109
|
const problems = views.flatMap((view) => {
|
|
104
110
|
const model = models[view.resource];
|
|
105
111
|
if (!model) return [];
|
|
106
|
-
const related = view.layout.flatMap((item) => item.kind === "related" ? relatedErrors(item, view.resource, models).map((error) => `related "${item.title}": ${error}`) : []);
|
|
112
|
+
const related = [...view.layout.flatMap((item) => item.kind === "related" ? relatedErrors(item, view.resource, models).map((error) => `related "${item.title}": ${error}`) : []), ...relatedColumnsErrors(view, model, models)];
|
|
107
113
|
const photos = [
|
|
108
114
|
...(view.list?.displays ?? []).flatMap((display) => display.kind === "cards" && display.photos ? cardPhotosErrors(display.photos, view.resource, models).map((error) => `display "${display.name}" photos: ${error}`) : []),
|
|
109
115
|
...sharedPhotosErrors(view),
|
package/dist/view-access.js
CHANGED
|
@@ -2,6 +2,7 @@ import { relatedErrors } from "./view-related.js";
|
|
|
2
2
|
import { cardPhotosErrors } from "./view-card-photos.js";
|
|
3
3
|
import { cardFavoriteErrors } from "./view-card-favorite.js";
|
|
4
4
|
import { searchesOfFor } from "./view-saved-search.js";
|
|
5
|
+
import { relatedColumnErrors } from "./view-related-column.js";
|
|
5
6
|
import { resolveFieldPath } from "@protobase/layout";
|
|
6
7
|
import { addressErrors, hrefPlaceholders, navRecentErrors, pruneByShape, shapes, tableDisplay } from "@protobase/schema/internal";
|
|
7
8
|
//#region src/view-access.ts
|
|
@@ -25,17 +26,22 @@ const pruneGallery = (item, resource, model, models) => {
|
|
|
25
26
|
...photos && { photos }
|
|
26
27
|
};
|
|
27
28
|
};
|
|
28
|
-
const
|
|
29
|
+
const tidyList = (model) => ({ default: opens, displays, columns, relatedColumns, ...list }) => {
|
|
29
30
|
const shown = displays && displays.length > 0 ? displays : void 0;
|
|
30
31
|
const kept = opens === tableDisplay || shown?.some((display) => display.name === opens);
|
|
32
|
+
const related = relatedColumns && relatedColumns.length > 0 ? relatedColumns : void 0;
|
|
33
|
+
const visible = columns.filter((name) => Object.hasOwn(model.fields, name) || related?.some((column) => column.name === name));
|
|
31
34
|
return {
|
|
32
35
|
...list,
|
|
36
|
+
columns: visible,
|
|
37
|
+
...related && { relatedColumns: related },
|
|
33
38
|
...shown && { displays: shown },
|
|
34
39
|
...opens !== void 0 && kept && { default: opens }
|
|
35
40
|
};
|
|
36
41
|
};
|
|
37
|
-
const listShape = (resource, models, prompted) => shapes.object({
|
|
38
|
-
columns: shapes.
|
|
42
|
+
const listShape = (resource, model, models, prompted) => shapes.object({
|
|
43
|
+
columns: shapes.data,
|
|
44
|
+
relatedColumns: shapes.list(shapes.map((column) => relatedColumnErrors(column, resource, models).length === 0 ? column : void 0)),
|
|
39
45
|
sort: shapes.list(shapes.tuple(shapes.field, shapes.data)),
|
|
40
46
|
search: shapes.list(shapes.field),
|
|
41
47
|
displays: shapes.list(shapes.oneOf({
|
|
@@ -81,7 +87,7 @@ const listShape = (resource, models, prompted) => shapes.object({
|
|
|
81
87
|
}, { requires: ["props"] })
|
|
82
88
|
})),
|
|
83
89
|
default: shapes.data
|
|
84
|
-
}, { after:
|
|
90
|
+
}, { after: tidyList(model) });
|
|
85
91
|
const widgetShape = shapes.oneOf({
|
|
86
92
|
range: shapes.object({
|
|
87
93
|
kind: shapes.data,
|
|
@@ -178,7 +184,7 @@ const viewShape = (resource, model, models, prompted) => shapes.object({
|
|
|
178
184
|
icon: shapes.data,
|
|
179
185
|
help: shapes.data,
|
|
180
186
|
fields: shapes.keys(),
|
|
181
|
-
list: listShape(resource, models, prompted),
|
|
187
|
+
list: listShape(resource, model, models, prompted),
|
|
182
188
|
filters: shapes.list(widgetShape),
|
|
183
189
|
layout: shapes.list(layoutShape(resource, model, models)),
|
|
184
190
|
chart: shapes.object({
|
|
@@ -201,7 +207,7 @@ const viewShape = (resource, model, models, prompted) => shapes.object({
|
|
|
201
207
|
/**
|
|
202
208
|
* The view with every reference to a field the caller cannot read removed: list columns, sort, search and displays,
|
|
203
209
|
* field hints, filter widgets, record sections, maps, galleries and custom components' props, the chart, title and search result, the
|
|
204
|
-
* actions that set or read one, and the sidebar's `recent` group. Nothing in it may name a hidden field. Related sections
|
|
210
|
+
* actions that set or read one, and the sidebar's `recent` group. Nothing in it may name a hidden field. Related sections, related list columns
|
|
205
211
|
* and links are checked against `models`, the caller's view of every resource, and so are `searchesOf`, cards' favorite
|
|
206
212
|
* buttons and the "Save search" button; the buttons stay for a visitor when their resource is one of `prompted`, those
|
|
207
213
|
* that ask the visitor to sign in.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { relatedErrors } from "./view-related.js";
|
|
2
|
+
//#region src/view-related-column.ts
|
|
3
|
+
const statusErrors = (target, name) => {
|
|
4
|
+
const field = Object.hasOwn(target.fields, name) ? target.fields[name] : void 0;
|
|
5
|
+
if (!field) return [`status: unknown field "${name}" on ${target.name}`];
|
|
6
|
+
if (field.type !== "enum") return [`status: "${name}" on ${target.name} is not an enum field`];
|
|
7
|
+
return field.sensitive ? [`status: "${name}" on ${target.name} is sensitive, so it cannot be shown`] : [];
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* What is wrong with a related records column of the list of `resource`, against these models; empty when it can be
|
|
11
|
+
* shown. It finds its records as a related section does. Against the full models it checks the config; against a
|
|
12
|
+
* caller's models, whether that caller can use it.
|
|
13
|
+
*/
|
|
14
|
+
const relatedColumnErrors = (column, resource, models) => {
|
|
15
|
+
const { name: _, label: __, status, ...match } = column;
|
|
16
|
+
const errors = relatedErrors({
|
|
17
|
+
kind: "related",
|
|
18
|
+
title: column.name,
|
|
19
|
+
...match
|
|
20
|
+
}, resource, models);
|
|
21
|
+
const target = models[column.resource];
|
|
22
|
+
return target ? [...errors, ...statusErrors(target, status)] : errors;
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { relatedColumnErrors };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/server",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.13",
|
|
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": {
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@better-auth/passkey": "^1.7.7",
|
|
33
|
-
"@protobase/layout": "0.19.
|
|
34
|
-
"@protobase/query": "0.19.
|
|
35
|
-
"@protobase/schema": "0.19.
|
|
33
|
+
"@protobase/layout": "0.19.13",
|
|
34
|
+
"@protobase/query": "0.19.13",
|
|
35
|
+
"@protobase/schema": "0.19.13",
|
|
36
36
|
"@react-email/render": "^2.1.0",
|
|
37
37
|
"@scalar/hono-api-reference": "^0.12.10",
|
|
38
38
|
"@sentry/core": "^11.7.0",
|