@protobase/server 0.68.2 → 0.68.4

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/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "0.68.2";
2
+ var version = "0.68.4";
3
3
  //#endregion
4
4
  export { version };
@@ -4,7 +4,7 @@ const reservedFields = ["id", "etag"];
4
4
  const isFieldType = (type) => definitionFieldTypes.includes(type);
5
5
  const fieldProblem = (field, columns, organization) => {
6
6
  if (!isUserTableName(field.name)) return `the field name ${JSON.stringify(field.name)} is not lowercase letters, digits and "_", starting with a letter, at most 63 bytes`;
7
- if (reservedFields.includes(field.name) || organization && field.name === "organization_id") return `the field name "${field.name}" is reserved`;
7
+ if (reservedFields.includes(field.name) || organization && field.name === organizationColumn) return `the field name "${field.name}" is reserved`;
8
8
  if (!isFieldType(field.type)) return `the field "${field.name}" has the type ${JSON.stringify(field.type)}, which is not one of ${definitionFieldTypes.join(", ")}`;
9
9
  if (field.type === "decimal" && (field.precision === null || field.scale === null)) return `the decimal field "${field.name}" has no precision and scale`;
10
10
  const column = columns.get(field.name);
@@ -1,5 +1,6 @@
1
- import { definitionFieldTypes, definitionsSchema, nameProblem, organizationColumn } from "./names.js";
1
+ import { definitionFieldTypes, definitionsSchema, maxOrganizationNameBytes, nameProblem, organizationColumn } from "./names.js";
2
2
  import { organizationAdmins } from "./organization-access.js";
3
+ import { maxLabelLength } from "@protobase/schema/internal";
3
4
  import { f, resource } from "@protobase/schema";
4
5
  //#region src/user-tables/definitions-resources.ts
5
6
  /** The names of the resources over the definitions: a table users add takes neither. */
@@ -36,14 +37,14 @@ const issue = (field, problem) => problem ? [{
36
37
  const definitionsResources = ({ roles, taken, organizations = false }) => {
37
38
  const organization = organizations ? { [organizationColumn]: f.text() } : {};
38
39
  const nameIssue = (name) => {
39
- if (organizations) return nameProblem(name, "table", 42);
40
+ if (organizations) return nameProblem(name, "table", maxOrganizationNameBytes);
40
41
  return nameProblem(name, "table") ?? (taken.includes(name) || ["user_tables", "user_table_fields"].includes(name) ? `"${name}" is the name of a resource of the app` : void 0);
41
42
  };
42
43
  const tables = resource(tablesResourceName).table(`${definitionsSchema}.tables`).fields({
43
44
  id: f.bigint().readOnly().dbDefault().filterable().sortable(),
44
- name: f.text().access(createOnly).filterable().sortable(),
45
- singular_label: f.text().max(200),
46
- plural_label: f.text().max(200),
45
+ name: f.text().access(createOnly),
46
+ singular_label: f.text().max(maxLabelLength),
47
+ plural_label: f.text().max(maxLabelLength),
47
48
  title_field: f.text().optional(),
48
49
  version: f.bigint().readOnly().dbDefault(),
49
50
  table_name: f.text().readOnly().dbDefault(),
@@ -52,7 +53,7 @@ const definitionsResources = ({ roles, taken, organizations = false }) => {
52
53
  const fields = resource(fieldsResourceName).table(`${definitionsSchema}.fields`).fields({
53
54
  table_id: f.relation(tablesResourceName).access(createOnly).filterable(),
54
55
  name: f.text().access(createOnly),
55
- label: f.text().max(200),
56
+ label: f.text().max(maxLabelLength),
56
57
  type: f.enum(definitionFieldTypes).access(createOnly),
57
58
  required: f.boolean().default(false).access(createOnly),
58
59
  position: f.integer().sortable(),
@@ -14,7 +14,8 @@ const fieldTypeLabels = {
14
14
  };
15
15
  /**
16
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.
17
+ * link to the table's own list, which shows once the app serves it. The title field is set once its field exists. Its
18
+ * Import makes a new table from a file, with its fields and records.
18
19
  */
19
20
  const tablesView = view(tablesResourceName).names({
20
21
  singular: "Table",
@@ -22,14 +23,14 @@ const tablesView = view(tablesResourceName).names({
22
23
  }).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
24
  creatorsOnly: true,
24
25
  order: 0
25
- }).fields((r) => ({
26
+ }).import("table").fields((r) => ({
26
27
  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
28
  plural_label: r.plural_label.label("Plural label").help("Its name in the sidebar and above its list, such as Notes."),
28
29
  singular_label: r.singular_label.label("Singular label").help("What one record is called, such as Note."),
29
30
  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
31
  })).list((r) => ({
31
32
  columns: [r.plural_label, r.name],
32
- sort: [[r.name, "asc"]]
33
+ sort: [[r.id, "asc"]]
33
34
  })).layout((r) => [l.section("Table", [
34
35
  r.plural_label,
35
36
  r.singular_label,
@@ -1,3 +1,4 @@
1
+ import "@protobase/schema/internal";
1
2
  //#region src/user-tables/names.d.ts
2
3
  /** The schema the definitions of the tables users add live in: `tables`, and `fields` of each. */
3
4
  export declare const definitionsSchema = "table_definitions";
@@ -1,11 +1,10 @@
1
- import { reservedWords } from "./reserved-words.js";
1
+ import { isUserTableName, maxNameBytes, maxNameBytes as maxNameBytes$1, maxOrganizationNameBytes, organizationColumn, userTableName } from "@protobase/schema/internal";
2
+ import { userTableNameIssue } from "@protobase/schema";
2
3
  //#region src/user-tables/names.ts
3
4
  /** The schema the definitions of the tables users add live in: `tables`, and `fields` of each. */
4
5
  const definitionsSchema = "table_definitions";
5
6
  /** The schema the tables users add live in, apart from their definitions, so no name of one reaches a definitions table. */
6
7
  const userTablesSchema = "user_tables";
7
- /** What a name of a user table or field must be: it reaches SQL as an identifier, always quoted too. */
8
- const userTableName = /^[a-z][a-z0-9_]*$/;
9
8
  /** The field types a definition may give, each with the Postgres types its column may have. */
10
9
  const definitionColumnTypes = {
11
10
  text: ["text"],
@@ -18,20 +17,12 @@ const definitionColumnTypes = {
18
17
  json: ["jsonb"]
19
18
  };
20
19
  const definitionFieldTypes = Object.keys(definitionColumnTypes);
21
- const isUserTableName = (name) => userTableName.test(name) && new TextEncoder().encode(name).length <= 63;
22
- /** The column that holds the organization of each record of a table users add with organizations, and of each definition. */
23
- const organizationColumn = "organization_id";
24
- /** The names a field never takes: the key's, the API's for the record version, and the organization's. */
25
- const reservedFieldNames = [
26
- "id",
27
- "etag",
28
- organizationColumn
29
- ];
30
20
  /** What is wrong with a name a table or field users add is given, or `undefined` when it may have it; `maxBytes` defaults to 63. */
31
- const nameProblem = (name, kind, maxBytes = 63) => {
32
- if (typeof name !== "string" || !isUserTableName(name) || new TextEncoder().encode(name).length > maxBytes) return `Use lowercase letters, digits and "_", starting with a letter, at most ${maxBytes} bytes`;
33
- if (reservedWords.has(name)) return `"${name}" is a word SQL reserves`;
34
- if (kind === "field" && reservedFieldNames.includes(name)) return `"${name}" is the name of a field every table has`;
21
+ const nameProblem = (name, kind, maxBytes = maxNameBytes) => {
22
+ const issue = userTableNameIssue(name, kind, maxBytes);
23
+ if (issue === "form") return `Use lowercase letters, digits and "_", starting with a letter, at most ${maxBytes} bytes`;
24
+ if (issue === "reserved_word") return `"${name}" is a word SQL reserves`;
25
+ if (issue === "reserved_field") return `"${name}" is the name of a field every table has`;
35
26
  };
36
27
  //#endregion
37
- export { definitionColumnTypes, definitionFieldTypes, definitionsSchema, isUserTableName, nameProblem, organizationColumn, reservedFieldNames, userTableName, userTablesSchema };
28
+ export { definitionColumnTypes, definitionFieldTypes, definitionsSchema, isUserTableName, maxNameBytes$1 as maxNameBytes, maxOrganizationNameBytes, nameProblem, organizationColumn, userTableName, userTablesSchema };
@@ -1,6 +1,6 @@
1
- import { definitionFieldTypes, definitionsSchema, userTableName, userTablesSchema } from "./names.js";
1
+ import { definitionFieldTypes, definitionsSchema, maxNameBytes, userTableName, userTablesSchema } from "./names.js";
2
2
  //#region src/user-tables/schema-sql.ts
3
- const nameCheck = (column) => `check (${column} ~ '${userTableName.source}' and octet_length(${column}) <= 63)`;
3
+ const nameCheck = (column) => `check (${column} ~ '${userTableName.source}' and octet_length(${column}) <= ${maxNameBytes})`;
4
4
  /**
5
5
  * The table of definitions: one row per table users add, its labels and a version, 1 when added, that each change of it
6
6
  * raises. With organizations a definition belongs to the one it was made in (`organization_id`), and its table is named
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/server",
3
- "version": "0.68.2",
3
+ "version": "0.68.4",
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.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",
47
+ "@protobase/layout": "0.68.4",
48
+ "@protobase/query": "0.68.4",
49
+ "@protobase/schema": "0.68.4",
50
+ "@protobase/workflow-plugins": "0.68.4",
51
+ "@protobase/workflows": "0.68.4",
52
52
  "@react-email/render": "^2.1.0",
53
53
  "@react-pdf/primitives": "^4.4.0",
54
54
  "@react-pdf/renderer": "^4.9.0",
@@ -1,87 +0,0 @@
1
- //#region src/user-tables/reserved-words.ts
2
- /**
3
- * The key words Postgres 18 reserves, `select word from pg_get_keywords() where catcode = 'R'`: a table or field users
4
- * add never takes one, so its name works unquoted in any SQL a developer writes against it.
5
- */
6
- const reservedWords = /* @__PURE__ */ new Set([
7
- "all",
8
- "analyse",
9
- "analyze",
10
- "and",
11
- "any",
12
- "array",
13
- "as",
14
- "asc",
15
- "asymmetric",
16
- "both",
17
- "case",
18
- "cast",
19
- "check",
20
- "collate",
21
- "column",
22
- "constraint",
23
- "create",
24
- "current_catalog",
25
- "current_date",
26
- "current_role",
27
- "current_time",
28
- "current_timestamp",
29
- "current_user",
30
- "default",
31
- "deferrable",
32
- "desc",
33
- "distinct",
34
- "do",
35
- "else",
36
- "end",
37
- "except",
38
- "false",
39
- "fetch",
40
- "for",
41
- "foreign",
42
- "from",
43
- "grant",
44
- "group",
45
- "having",
46
- "in",
47
- "initially",
48
- "intersect",
49
- "into",
50
- "lateral",
51
- "leading",
52
- "limit",
53
- "localtime",
54
- "localtimestamp",
55
- "not",
56
- "null",
57
- "offset",
58
- "on",
59
- "only",
60
- "or",
61
- "order",
62
- "placing",
63
- "primary",
64
- "references",
65
- "returning",
66
- "select",
67
- "session_user",
68
- "some",
69
- "symmetric",
70
- "system_user",
71
- "table",
72
- "then",
73
- "to",
74
- "trailing",
75
- "true",
76
- "union",
77
- "unique",
78
- "user",
79
- "using",
80
- "variadic",
81
- "when",
82
- "where",
83
- "window",
84
- "with"
85
- ]);
86
- //#endregion
87
- export { reservedWords };