@owlmeans/error 0.1.18-rc.1 → 0.1.18-rc.11

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/README.md CHANGED
@@ -11,7 +11,7 @@ Serializable error base class with type registration for cross-process error pro
11
11
  ## Installation
12
12
 
13
13
  ```bash
14
- bun add @owlmeans/error
14
+ bun add @owlmeans/error@^0.1.18-rc.7
15
15
  ```
16
16
 
17
17
  ## Usage
@@ -83,7 +83,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
83
83
  your project's skill store (`.agents/skills/`):
84
84
 
85
85
  ```sh
86
- npx @owlmeans/agent-skills
86
+ npx @owlmeans/agent-skills@^0.1.18-rc.15
87
87
  ```
88
88
 
89
89
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/error",
4
- "version": "0.1.18-rc.0",
5
- "generatedAt": "2026-08-16T22:20:50.513Z",
4
+ "version": "0.1.18-rc.11",
5
+ "generatedAt": "2026-09-12T08:39:52.734Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: error
3
- description: How to use @owlmeans/error — base error classes (ResilientError), error normalization, and i18n-aware error types. Auto-invoked when importing from this package or throwing/catching framework errors.
3
+ description: How to use @owlmeans/error — ResilientError, the error class registry, marshalling errors across a service boundary and back, and the i18n namespace error messages resolve through. Auto-invoked when importing from this package, declaring a typed framework error, or normalizing a caught error.
4
4
  user-invocable: false
5
5
  ---
6
6
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
@@ -8,44 +8,111 @@ user-invocable: false
8
8
  # @owlmeans/error
9
9
 
10
10
  **Layer:** Core
11
- **Install:** `"@owlmeans/error": "^0.1.18-rc.0"` in `dependencies`
11
+ **Install:** `"@owlmeans/error": "^0.1.18-rc.11"` in `dependencies`
12
12
 
13
13
  ## Key Exports
14
14
 
15
15
  | Export | Description |
16
16
  |--------|-------------|
17
- | `ResilientError` | Base error class — all framework errors extend this |
18
- | `ErrorNormalizer` | Normalize unknown errors into ResilientError instances |
19
- | `ErrorTypes` | Built-in error type aliases |
20
- | i18n helpers | Resolve error messages through the i18n layer |
17
+ | `ResilientError` | Base class — every framework error extends it |
18
+ | `ResilientError.registerErrorClass(Class)` | Register a subclass so it survives a round trip |
19
+ | `ResilientError.ensure(err)` | Turn anything caught into a `ResilientError` |
20
+ | `ResilientError.marshal(err)` / `err.marshal()` | Flatten into a plain `Error` a transport can carry |
21
+ | `enuserError(err)` | `ensure`, typed to the subclass you expect |
22
+ | `marshalError(err)` | `ensure` then `marshal`, for a boundary that only sends `Error` |
23
+ | `SEPARATOR` (`'\|\|\|'`), `RESILENT_ERROR` | The marshalling separator and the base type name |
24
+ | `Converter` | `{ match, convert, isMarshaled, unmarshal }` — one registry entry |
25
+ | `ResilientErrorConstructor` | The constructor shape `registerErrorClass` accepts |
26
+ | `ValueOrError<T>` | `T \| ResilientError`, for a result that carries either |
21
27
 
22
- ## Usage
28
+ ## Declaring an error
23
29
 
24
- Subclass `ResilientError` for typed framework errors with i18n-resolvable messages:
30
+ A subclass owns a static `typeName` and prefixes its messages, so the pair `type` + `message` is
31
+ enough to identify what went wrong anywhere the error travels. **Register it** — registration is
32
+ what makes a marshaled error come back as the class it was thrown as. Skip it and the far side gets
33
+ an unusable `ResilientError` whose `type` is the whole marshaled string (see below).
25
34
 
26
35
  ```typescript
27
36
  import { ResilientError } from '@owlmeans/error'
28
37
 
29
- export class AgentApiError extends ResilientError {
30
- public static override typeName = 'AgentApiError'
31
- public constructor(message: string = 'unknown') {
32
- super(AgentApiError.typeName, `agent-api:${message}`)
38
+ export class ApiError extends ResilientError {
39
+ public static override typeName = 'ApiError'
40
+
41
+ constructor(message: string = 'error') {
42
+ super(ApiError.typeName, `api:${message}`)
43
+ }
44
+ }
45
+
46
+ export class RateLimitError extends ApiError {
47
+ public static override typeName = `${ApiError.typeName}:RateLimit`
48
+
49
+ constructor(message: string = 'error') {
50
+ super(`rate-limit:${message}`)
51
+ this.type = RateLimitError.typeName
33
52
  }
34
53
  }
35
54
 
36
- // In a handler:
37
- throw new AgentApiError('rate-limited')
55
+ ResilientError.registerErrorClass(ApiError)
56
+ ResilientError.registerErrorClass(RateLimitError)
57
+
58
+ throw new RateLimitError('per-minute')
38
59
  ```
39
60
 
40
- Catch and normalize:
61
+ A subclass of a subclass calls `super` with the message alone and then re-stamps `this.type` — the
62
+ parent supplies its own prefix, so the final message reads `api:rate-limit:per-minute`.
63
+
64
+ `registerErrorClass` takes a second, native-class argument, and `ensure` takes a second
65
+ `throwOnUnknown` argument. **Neither has any effect** — a catch-all converter is pushed onto the
66
+ registry when this package loads and it is the first entry `ensure` tests for conversion, so no
67
+ later converter and no `throwOnUnknown` branch is ever reached. Register the class alone, and treat
68
+ `ensure` as taking one argument.
69
+
70
+ ## Normalizing what you caught
71
+
72
+ `ensure` gives you a `ResilientError` for anything caught, but only a **registered, marshaled**
73
+ error survives with its identity intact. Route errors that must keep their type through
74
+ `marshal`/`ensure`; do not rely on `ensure` alone to normalize an arbitrary throw.
75
+
41
76
  ```typescript
42
- import { ErrorNormalizer } from '@owlmeans/error'
43
- try { ... } catch (e) {
44
- const err = ErrorNormalizer.normalize(e)
45
- // err is now a ResilientError with stable .type and .message
77
+ try { /* ... */ } catch (e) {
78
+ const err = ResilientError.ensure(e as Error)
79
+ if (err instanceof RateLimitError) { /* the registered class came back */ }
46
80
  }
47
81
  ```
48
82
 
83
+ What `ensure` actually does, in order:
84
+
85
+ | Input | Result |
86
+ |-------|--------|
87
+ | A `ResilientError` | returned untouched |
88
+ | A `SyntaxError` | rethrown — never converted |
89
+ | An `Error` marshaled from a **registered** class | unmarshaled into that class, `type` and `message` restored |
90
+ | Anything else | a bare `ResilientError` whose **`type` is the original `message`** and whose **`message` is the original stack** |
91
+
92
+ That last row is the trap: the fields are shifted, so an unregistered marshaled error arrives with
93
+ `type` set to the whole `Type|||message|||stack` string, and a plain `new Error('boom')` arrives with
94
+ `type: 'boom'`. Read `.type` only where the error came back through the registered path; keep the
95
+ original around when you need its message.
96
+
97
+ **`SyntaxError` is never converted — it is rethrown.** A `SyntaxError` in this framework means the
98
+ process is wired wrong (an unknown alias, a missing service, a route cycle), and it must crash
99
+ rather than reach a user as a handled failure. Do not throw one for a runtime condition a caller is
100
+ expected to handle.
101
+
102
+ ## Crossing a service boundary
103
+
104
+ `marshal` flattens `type`, `message` and the original stack into one `Error` message joined by
105
+ `SEPARATOR`. On the far side `ensure` recognises the prefix and rebuilds the registered class, so a
106
+ typed error thrown in a backend is caught as the same class in a client. Override
107
+ `finalizeUnmarshal()` on a subclass that needs to rebuild state from its message after that.
108
+
109
+ ## Messages are i18n keys
110
+
111
+ Importing this package registers the `errors` translation library for every bundled locale. UIs
112
+ resolve an error by its `type` — `errors.<type>`, with a form- or screen-scoped key tried first —
113
+ so the message a user reads comes from the translations, never from the thrown string. Ship a
114
+ translation for each error type you declare.
115
+
49
116
  ## Depends On
50
117
 
51
- - `@owlmeans/i18n` — for resolving error messages by key
118
+ - `@owlmeans/i18n` — the translation library the `errors` namespace is registered in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/error",
3
- "version": "0.1.18-rc.1",
3
+ "version": "0.1.18-rc.11",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -21,7 +21,7 @@
21
21
  }
22
22
  },
23
23
  "dependencies": {
24
- "@owlmeans/i18n": "^0.1.18-rc.1"
24
+ "@owlmeans/i18n": "^0.1.18-rc.11"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@owlmeans/dep-config": "workspace:*",
package/build/.gitkeep DELETED
File without changes