@protobase/server 0.53.4 → 0.53.6

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/author.js ADDED
@@ -0,0 +1,16 @@
1
+ //#region src/author.ts
2
+ /**
3
+ * The values of a create with its `.author()` field set to the signed-in user's id. No request may write the field
4
+ * (access resolution never counts it writable, so validation refuses it as read-only), so this is its only source; a
5
+ * create by no one signed in, such as a public form's, leaves it empty.
6
+ */
7
+ const withAuthor = (access, values) => {
8
+ const { author } = access.full;
9
+ if (!author || access.session.anonymous) return values;
10
+ return {
11
+ ...values,
12
+ [author]: access.session.user.id
13
+ };
14
+ };
15
+ //#endregion
16
+ export { withAuthor };
@@ -1,7 +1,7 @@
1
1
  //#region src/check-owners.ts
2
2
  const holds = (field, id) => field === "text" || field === id || field === "bigint" && id === "integer";
3
3
  /**
4
- * Stops startup on an `.owner()` field that cannot hold the ids of the signed-in users. The authenticator's
4
+ * Stops startup on an `.owner()` or `.author()` field that cannot hold the ids of the signed-in users. The authenticator's
5
5
  * `userIdType` declares their type; without it, the auth instance's users have Better Auth's own ids, random text,
6
6
  * unless `generateUserId` makes the ids. Without either, the ids come from the app's own authenticator, which this
7
7
  * cannot see.
@@ -13,8 +13,19 @@ const checkOwnerFields = (registry, userIdType, auth) => {
13
13
  };
14
14
  const checkAgainst = (registry, userIdType, fix) => {
15
15
  for (const entry of registry.entries) {
16
- if (entry.ownerType === void 0 || holds(entry.ownerType, userIdType)) continue;
17
- throw new Error(`${entry.name}.${entry.model.owner} is the owner of its records, so it holds the signed-in user's id, but it holds ${entry.ownerType} values and ${fix(entry.ownerType)}`);
16
+ const fields = [{
17
+ role: "owner",
18
+ name: entry.model.owner,
19
+ type: entry.ownerType
20
+ }, {
21
+ role: "author",
22
+ name: entry.model.author,
23
+ type: entry.authorType
24
+ }];
25
+ for (const { role, name, type } of fields) {
26
+ if (type === void 0 || holds(type, userIdType)) continue;
27
+ throw new Error(`${entry.name}.${name} is the ${role} of its records, so it holds the signed-in user's id, but it holds ${type} values and ${fix(type)}`);
28
+ }
18
29
  }
19
30
  };
20
31
  //#endregion
package/dist/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "0.53.4";
2
+ var version = "0.53.6";
3
3
  //#endregion
4
4
  export { version };
@@ -12,6 +12,8 @@ export type Entry = {
12
12
  keyTypes: KeyType[];
13
13
  /** The type of the user ids its `.owner()` field holds: the field's own, or the key type of the record it points at. */
14
14
  ownerType?: KeyType;
15
+ /** The type of the user ids its `.author()` field holds, as `ownerType` for the owner. */
16
+ authorType?: KeyType;
15
17
  /** A database-managed `version` or `updated_at` column, when the table has one; otherwise ETags hash the row. */
16
18
  etag?: EtagSource;
17
19
  };
package/dist/registry.js CHANGED
@@ -14,8 +14,8 @@ const etagSource = (model) => {
14
14
  };
15
15
  };
16
16
  const isKeyType = (type) => type === "text" || type === "integer" || type === "bigint" || type === "uuid";
17
- const ownerType = (model, resolve) => {
18
- const field = model.owner === void 0 ? void 0 : model.fields[model.owner];
17
+ const userIdType = (model, name, resolve) => {
18
+ const field = name === void 0 ? void 0 : model.fields[name];
19
19
  if (!field) return void 0;
20
20
  if (isKeyType(field.type)) return field.type;
21
21
  const target = field.type === "relation" && field.relation ? resolve(field.relation.resource) : void 0;
@@ -33,13 +33,15 @@ const buildRegistry = (resources) => {
33
33
  const types = keyTypes(model, (name) => models.get(name));
34
34
  if (types.length !== model.primaryKey.length) throw new Error(`Not implemented: resource "${model.name}" has a primary key field that points at a composite-key resource; the server maps one key part per key field`);
35
35
  const etag = etagSource(model);
36
- const owner = ownerType(model, (name) => models.get(name));
36
+ const owner = userIdType(model, model.owner, (name) => models.get(name));
37
+ const author = userIdType(model, model.author, (name) => models.get(name));
37
38
  return {
38
39
  name: model.name,
39
40
  source,
40
41
  model,
41
42
  keyTypes: types,
42
43
  ...owner && { ownerType: owner },
44
+ ...author && { authorType: author },
43
45
  ...etag && { etag }
44
46
  };
45
47
  });
@@ -13,6 +13,7 @@ import { encryptedValuesOf, hasEncryptedValues, sealValues } from "./encryption/
13
13
  import { validateBody } from "./validate-body.js";
14
14
  import { applyUpdate, planUpdate } from "./write-update.js";
15
15
  import { checkRankAfter } from "./rank-after.js";
16
+ import { withAuthor } from "./author.js";
16
17
  //#region src/write-pipeline.ts
17
18
  const withOwner = (access, values) => {
18
19
  const { owner } = access.full;
@@ -37,7 +38,7 @@ const create = async (trx, deps, access, body, removed) => {
37
38
  const tenantValues = full.tenant && scope ? { [full.tenant]: scope.tenantValue } : {};
38
39
  const preset = typeof body === "object" && body !== null && !Array.isArray(body) && writableFields(access, "create").includes(full.owner ?? "") ? withOwner(access, body) : body;
39
40
  const row = {
40
- ...await attachFiles(access, withOwner(access, validateBody(access, "create", preset, tenantValues))),
41
+ ...await attachFiles(access, withAuthor(access, withOwner(access, validateBody(access, "create", preset, tenantValues)))),
41
42
  ...tenantValues
42
43
  };
43
44
  const filter = await decide(access, "create", void 0, row);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/server",
3
- "version": "0.53.4",
3
+ "version": "0.53.6",
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": {
@@ -40,9 +40,9 @@
40
40
  "@better-auth/passkey": "^1.7.12",
41
41
  "@better-auth/sso": "^1.7.12",
42
42
  "@modelcontextprotocol/server": "^2.3.1",
43
- "@protobase/layout": "0.53.4",
44
- "@protobase/query": "0.53.4",
45
- "@protobase/schema": "0.53.4",
43
+ "@protobase/layout": "0.53.6",
44
+ "@protobase/query": "0.53.6",
45
+ "@protobase/schema": "0.53.6",
46
46
  "@react-email/render": "^2.1.0",
47
47
  "@scalar/hono-api-reference": "^0.12.10",
48
48
  "@sentry/core": "^11.7.0",