@ultimat3/entity 9.0.0 → 10.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultimat3/entity",
3
- "version": "9.0.0",
3
+ "version": "10.0.0",
4
4
  "description": "A table + its domain type + invariants the database also enforces",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,9 +31,9 @@
31
31
  "test": "bun test"
32
32
  },
33
33
  "dependencies": {
34
- "@ultimat3/core": "9.0.0",
35
- "@ultimat3/db": "9.0.0",
36
- "@ultimat3/schema": "9.0.0",
37
- "@ultimat3/time": "9.0.0"
34
+ "@ultimat3/core": "10.0.0",
35
+ "@ultimat3/db": "10.0.0",
36
+ "@ultimat3/schema": "10.0.0",
37
+ "@ultimat3/time": "10.0.0"
38
38
  }
39
39
  }
package/src/entity.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  // (the typed db handle, migrations, cache tags, the admin UI, the manifest) is projected from
4
4
  // this one call.
5
5
 
6
+ import { renderThrowable } from '@ultimat3/core';
6
7
  import { describeValue, type StandardSchemaV1 } from '@ultimat3/schema';
7
8
  import { entityNow } from './clock';
8
9
  import { assertColumnName, bindColumn, columnName, moneyColumns } from './column';
@@ -284,9 +285,13 @@ export const entity = <const C extends ColumnMap>(
284
285
  try {
285
286
  return { value: parse(value) };
286
287
  } catch (error) {
287
- return {
288
- issues: [{ message: error instanceof Error ? error.message : String(error) }],
289
- };
288
+ // `renderThrowable`, never `error instanceof Error ? error.message : String(error)`:
289
+ // both halves of that read the caught value directly. `instanceof` consults
290
+ // `getPrototypeOf` and `String()` runs the value's own coercion, so a `Proxy` or a
291
+ // null-prototype throwable raised a SECOND, uncatchable `TypeError` out of the
292
+ // validator — where a rejection belongs. A column parser is app-reachable and an
293
+ // app's `$parse` may throw anything at all.
294
+ return { issues: [{ message: renderThrowable(error) }] };
290
295
  }
291
296
  },
292
297
  },
package/src/errors.ts CHANGED
@@ -61,6 +61,14 @@ registerErrorCodes(
61
61
  Object.fromEntries(Object.entries(ENTITY_ERROR_TITLES).map(([code, title]) => [code, { title }])),
62
62
  );
63
63
 
64
+ /**
65
+ * Base for every error this package throws. No `docs:` — `UltimateError` fills it from
66
+ * `describeErrorCode(code).docs`, which is `@ultimat3/core`'s `ERROR_DOCS_URL`: one page for every
67
+ * code, never one per code, because `wiki/` is the framework's only public documentation surface
68
+ * and a code lives there in a TABLE ROW, which has no anchor. The
69
+ * `https://ultimate.dev/errors/<code>` links this class built until 9.x answered 404, host
70
+ * included, on every refusal it has ever raised.
71
+ */
64
72
  export class EntityError extends UltimateError {
65
73
  override readonly name = 'EntityError';
66
74
 
@@ -69,7 +77,6 @@ export class EntityError extends UltimateError {
69
77
  code: init.code,
70
78
  cause: init.cause,
71
79
  fix: init.fix,
72
- docs: `https://ultimate.dev/errors/${init.code}`,
73
80
  });
74
81
  }
75
82
  }
package/src/view.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  // already describe. Values are validated by the entity's own column parsers; an unknown key is a
4
4
  // declaration-time failure, not a surprise on the first request.
5
5
 
6
+ import { renderThrowable } from '@ultimat3/core';
6
7
  import { describeValue, type StandardSchemaV1 } from '@ultimat3/schema';
7
8
  import { invariantViolated } from './errors';
8
9
  import type { AnyColumn, ColumnMap } from './types';
@@ -83,9 +84,10 @@ export const viewFor = <Row, K extends keyof Row & string>(
83
84
  try {
84
85
  return { value: parse(value) };
85
86
  } catch (error) {
86
- return {
87
- issues: [{ message: error instanceof Error ? error.message : String(error) }],
88
- };
87
+ // The entity's rule, for the same reason: `instanceof` and `String()` are both reads of
88
+ // a caught value, and a throwable that fights being read turned a rejected projection
89
+ // into an uncatchable `TypeError`. `renderThrowable` is total.
90
+ return { issues: [{ message: renderThrowable(error) }] };
89
91
  }
90
92
  },
91
93
  },