@danypops/tickets 0.8.4 → 0.8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/tickets",
3
- "version": "0.8.4",
3
+ "version": "0.8.6",
4
4
  "description": "Unified CLI, daemon, and TypeScript library for issue tracking across GitHub, GitLab, and Jira.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -24,8 +24,8 @@
24
24
  "typecheck": "tsc --noEmit"
25
25
  },
26
26
  "dependencies": {
27
- "@danypops/vehicle-core": "^0.10.0",
28
- "@danypops/vehicle-server": "^0.13.0",
27
+ "@danypops/vehicle-core": "^0.12.1",
28
+ "@danypops/vehicle-server": "^0.17.0",
29
29
  "@danypops/vehicle-client": "^0.5.0",
30
30
  "@danypops/enigma-client": "^0.3.0",
31
31
  "@gitbeaker/rest": "^43.8.0",
@@ -16,6 +16,7 @@
16
16
  */
17
17
  import {
18
18
  bindVehicleOperation,
19
+ defineErrorMapping,
19
20
  defineLooseObjectSchema,
20
21
  defineVehicleOperation,
21
22
  type LooseObjectProperty,
@@ -24,11 +25,21 @@ import {
24
25
  } from "@danypops/vehicle-core";
25
26
  import { VehicleRegistry } from "@danypops/vehicle-server";
26
27
  import type { BackendCapabilities, TicketService } from "../issue/service.js";
28
+ import { statusForKnownTicketError } from "../rpc/error-status.js";
27
29
  import type { TicketOperation } from "../rpc/ops.js";
28
30
  import { TICKET_OP_HANDLERS, type TicketsAppDeps } from "../rpc/server.js";
29
31
 
30
32
  const OWNER = "tickets";
31
33
 
34
+ const withTicketsErrorParity = defineErrorMapping(
35
+ [
36
+ { matches: (error) => statusForKnownTicketError(error) === 404, category: "not_found" },
37
+ { matches: (error) => statusForKnownTicketError(error) === 400, category: "validation" },
38
+ { matches: (error) => statusForKnownTicketError(error) === 422, category: "authorization" },
39
+ ],
40
+ { fallbackCategory: "internal", fallbackCode: "handler-failed", fallbackMessage: "Tickets operation failed" },
41
+ );
42
+
32
43
  const LIMITS = { defaultTimeoutMs: 10_000, maxTimeoutMs: 30_000, maxRequestBytes: 65_536, maxResponseBytes: 262_144 };
33
44
 
34
45
  const stringProp: LooseObjectProperty = { type: "string" };
@@ -285,10 +296,11 @@ export function syncDiscoverAvailability(registry: VehicleRegistry, service: Tic
285
296
  export function createTicketsVehicleRegistry(deps: Omit<TicketsAppDeps, "vehicleRegistry">): VehicleRegistry {
286
297
  const registry = new VehicleRegistry({
287
298
  name: "tickets",
288
- version: "1.0.0",
299
+ version: deps.version,
289
300
  description: "Unified issue tracking across GitHub, GitLab, and Jira.",
290
301
  });
291
-
302
+ // Every handler passes through the reviewed mapper above; unmatched failures stay redacted.
303
+ registry.setExposeHandlerFailureDetails(true);
292
304
  for (const spec of OPERATIONS) {
293
305
  const operation = defineVehicleOperation({
294
306
  name: spec.action,
@@ -305,7 +317,11 @@ export function createTicketsVehicleRegistry(deps: Omit<TicketsAppDeps, "vehicle
305
317
  const mapInput = spec.mapInput ?? ((input: Record<string, unknown>) => input);
306
318
  registry.register(
307
319
  OWNER,
308
- bindVehicleOperation(operation, () => async (context) => handler(deps, mapInput(context.input as Record<string, unknown>) as never)),
320
+ bindVehicleOperation(
321
+ operation,
322
+ () => async (context) =>
323
+ withTicketsErrorParity<unknown>(() => handler(deps, mapInput(context.input as Record<string, unknown>) as never)),
324
+ ),
309
325
  );
310
326
  }
311
327
 
@@ -0,0 +1,12 @@
1
+ import { AuthRequiredError, IssueNotFoundError } from "../issue/errors.js";
2
+ import { NotSupportedError, UnknownBackendError } from "../issue/service.js";
3
+ import { FocusError } from "../sqlite/focus.js";
4
+ import { SavedQueryNotFoundError } from "../sqlite/saved-queries.js";
5
+
6
+ /** Returns the legacy HTTP status only for reviewed business errors; unknown failures stay unclassified. */
7
+ export function statusForKnownTicketError(error: unknown): number | undefined {
8
+ if (error instanceof IssueNotFoundError || error instanceof SavedQueryNotFoundError) return 404;
9
+ if (error instanceof UnknownBackendError || error instanceof NotSupportedError || error instanceof FocusError) return 400;
10
+ if (error instanceof AuthRequiredError) return 422;
11
+ return undefined;
12
+ }
package/src/rpc/server.ts CHANGED
@@ -11,12 +11,12 @@ import type { VehicleRegistry } from "@danypops/vehicle-server";
11
11
  import { createVehicleHttpApp } from "@danypops/vehicle-server/http";
12
12
  import type { Logger } from "@danypops/vehicle-server/logging";
13
13
  import { errorResponse, healthResponse, jsonResponse, readyResponse, requireBearerToken } from "@danypops/vehicle-server/rpc-http";
14
- import { AuthRequiredError, IssueNotFoundError } from "../issue/errors.js";
15
14
  import { parseRef } from "../issue/issue.js";
16
- import { NotSupportedError, type TicketService, UnknownBackendError } from "../issue/service.js";
15
+ import type { TicketService } from "../issue/service.js";
17
16
  import { FocusError, type FocusStore } from "../sqlite/focus.js";
18
17
  import type { Ledger } from "../sqlite/ledger.js";
19
18
  import { SavedQueryNotFoundError, type SavedQueryStore } from "../sqlite/saved-queries.js";
19
+ import { statusForKnownTicketError } from "./error-status.js";
20
20
  import { TICKET_OPERATIONS, type TicketOperation, type TicketOpInputs, type TicketOpOutputs } from "./ops.js";
21
21
 
22
22
  export interface TicketsAppDeps {
@@ -117,10 +117,7 @@ function isTicketOperation(value: unknown): value is TicketOperation {
117
117
  }
118
118
 
119
119
  function statusFor(error: unknown): number {
120
- if (error instanceof IssueNotFoundError || error instanceof SavedQueryNotFoundError) return 404;
121
- if (error instanceof UnknownBackendError || error instanceof NotSupportedError || error instanceof FocusError) return 400;
122
- if (error instanceof AuthRequiredError) return 422;
123
- return 500;
120
+ return statusForKnownTicketError(error) ?? 500;
124
121
  }
125
122
 
126
123
  export function buildApp(deps: TicketsAppDeps): { fetch(request: Request): Promise<Response> } {