@danypops/vehicle-core 0.12.1 → 0.12.3

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.
@@ -59,6 +59,8 @@ export declare class VehicleError extends Error {
59
59
  constructor(code: string, message: string, options: VehicleErrorOptions);
60
60
  toFailure(): VehicleFailure;
61
61
  }
62
+ /** Recognizes VehicleError instances across duplicated package installations in one process. */
63
+ export declare function isVehicleError(value: unknown): value is VehicleError;
62
64
  /** Extracts a bounded, wire-safe message from an unknown cause -- never the full stack trace, never an unbounded payload. */
63
65
  export declare function boundedCauseMessage(cause: unknown): string | undefined;
64
66
  export declare function boundedValidationDetails(issues: readonly VehicleSchemaIssue[] | undefined): JsonValue | undefined;
@@ -1,3 +1,4 @@
1
+ const VEHICLE_ERROR_BRAND = Symbol.for("@danypops/vehicle-core/VehicleError");
1
2
  /** Maps reviewed domain errors into wire-safe Vehicle errors while preserving already-mapped failures. */
2
3
  export function defineErrorMapping(rules, options = {}) {
3
4
  return async (run) => {
@@ -5,12 +6,13 @@ export function defineErrorMapping(rules, options = {}) {
5
6
  return await run();
6
7
  }
7
8
  catch (error) {
8
- if (error instanceof VehicleError)
9
+ if (isVehicleError(error))
9
10
  throw error;
10
11
  const rule = rules.find((candidate) => "errorClass" in candidate ? error instanceof candidate.errorClass : candidate.matches(error));
11
12
  const causeMessage = error instanceof Error ? error.message : String(error);
12
13
  const message = rule === undefined && options.fallbackMessage !== undefined ? options.fallbackMessage : causeMessage;
13
- throw new VehicleError(rule?.code ?? options.fallbackCode ?? "operation-rejected", message, {
14
+ const code = rule === undefined ? (options.fallbackCode ?? "operation-rejected") : (rule.code ?? "operation-rejected");
15
+ throw new VehicleError(code, message, {
14
16
  category: rule?.category ?? options.fallbackCategory ?? "validation",
15
17
  cause: error,
16
18
  });
@@ -29,6 +31,7 @@ export class VehicleError extends Error {
29
31
  constructor(code, message, options) {
30
32
  super(message, options.cause === undefined ? undefined : { cause: options.cause });
31
33
  this.code = code;
34
+ Object.defineProperty(this, VEHICLE_ERROR_BRAND, { value: true });
32
35
  this.name = "VehicleError";
33
36
  this.category = options.category;
34
37
  this.retryable = options.retryable ?? false;
@@ -53,6 +56,10 @@ export class VehicleError extends Error {
53
56
  };
54
57
  }
55
58
  }
59
+ /** Recognizes VehicleError instances across duplicated package installations in one process. */
60
+ export function isVehicleError(value) {
61
+ return value instanceof Error && Reflect.get(value, VEHICLE_ERROR_BRAND) === true;
62
+ }
56
63
  const MAX_CAUSE_MESSAGE_LENGTH = 500;
57
64
  /** Extracts a bounded, wire-safe message from an unknown cause -- never the full stack trace, never an unbounded payload. */
58
65
  export function boundedCauseMessage(cause) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/vehicle-core",
3
- "version": "0.12.1",
3
+ "version": "0.12.3",
4
4
  "description": "Vehicle's runtime-neutral wire contract: operation descriptors, schema codecs, failure shapes. Zero runtime dependencies, zero Bun-specific code -- the one thing every Vehicle client and server package depends on.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,5 +1,7 @@
1
1
  import type { JsonValue, VehicleSchemaIssue } from "./vehicle-contract.js";
2
2
 
3
+ const VEHICLE_ERROR_BRAND = Symbol.for("@danypops/vehicle-core/VehicleError");
4
+
3
5
  export type VehicleFailureCategory =
4
6
  | "validation"
5
7
  | "not_found"
@@ -92,13 +94,14 @@ export function defineErrorMapping(
92
94
  try {
93
95
  return await run();
94
96
  } catch (error) {
95
- if (error instanceof VehicleError) throw error;
97
+ if (isVehicleError(error)) throw error;
96
98
  const rule = rules.find((candidate) =>
97
99
  "errorClass" in candidate ? error instanceof candidate.errorClass : candidate.matches(error),
98
100
  );
99
101
  const causeMessage = error instanceof Error ? error.message : String(error);
100
102
  const message = rule === undefined && options.fallbackMessage !== undefined ? options.fallbackMessage : causeMessage;
101
- throw new VehicleError(rule?.code ?? options.fallbackCode ?? "operation-rejected", message, {
103
+ const code = rule === undefined ? (options.fallbackCode ?? "operation-rejected") : (rule.code ?? "operation-rejected");
104
+ throw new VehicleError(code, message, {
102
105
  category: rule?.category ?? options.fallbackCategory ?? "validation",
103
106
  cause: error,
104
107
  });
@@ -121,6 +124,7 @@ export class VehicleError extends Error {
121
124
  options: VehicleErrorOptions,
122
125
  ) {
123
126
  super(message, options.cause === undefined ? undefined : { cause: options.cause });
127
+ Object.defineProperty(this, VEHICLE_ERROR_BRAND, { value: true });
124
128
  this.name = "VehicleError";
125
129
  this.category = options.category;
126
130
  this.retryable = options.retryable ?? false;
@@ -147,6 +151,11 @@ export class VehicleError extends Error {
147
151
  }
148
152
  }
149
153
 
154
+ /** Recognizes VehicleError instances across duplicated package installations in one process. */
155
+ export function isVehicleError(value: unknown): value is VehicleError {
156
+ return value instanceof Error && Reflect.get(value, VEHICLE_ERROR_BRAND) === true;
157
+ }
158
+
150
159
  const MAX_CAUSE_MESSAGE_LENGTH = 500;
151
160
 
152
161
  /** Extracts a bounded, wire-safe message from an unknown cause -- never the full stack trace, never an unbounded payload. */