@voyantjs/bookings 0.5.0 → 0.6.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/dist/routes.js CHANGED
@@ -1,8 +1,8 @@
1
- import { createKmsProviderFromEnv } from "@voyantjs/utils";
1
+ import { ForbiddenApiError, handleApiError, normalizeValidationError, parseJsonBody, parseQuery, requireUserId, UnauthorizedApiError, } from "@voyantjs/hono";
2
2
  import { Hono } from "hono";
3
3
  import { createBookingPiiService } from "./pii.js";
4
+ import { BOOKING_ROUTE_RUNTIME_CONTAINER_KEY, buildBookingRouteRuntime, } from "./route-runtime.js";
4
5
  import { bookingGroupRoutes } from "./routes-groups.js";
5
- import { getRuntimeEnv } from "./routes-shared.js";
6
6
  import { bookingPiiAccessLog } from "./schema.js";
7
7
  import { bookingsService } from "./service.js";
8
8
  import { bookingGroupsService } from "./service-groups.js";
@@ -43,7 +43,10 @@ async function authorizeBookingPiiAccess(c, input) {
43
43
  outcome: "denied",
44
44
  reason: "missing_user",
45
45
  });
46
- return { allowed: false, response: c.json({ error: "Unauthorized" }, 401) };
46
+ return {
47
+ allowed: false,
48
+ response: handleApiError(new UnauthorizedApiError(), c),
49
+ };
47
50
  }
48
51
  const customAuthorizer = c.get("authorizeBookingPii");
49
52
  if (customAuthorizer) {
@@ -62,7 +65,10 @@ async function authorizeBookingPiiAccess(c, input) {
62
65
  outcome: "denied",
63
66
  reason: "custom_policy_denied",
64
67
  });
65
- return { allowed: false, response: c.json({ error: "Forbidden" }, 403) };
68
+ return {
69
+ allowed: false,
70
+ response: handleApiError(new ForbiddenApiError(), c),
71
+ };
66
72
  }
67
73
  return { allowed: true };
68
74
  }
@@ -76,7 +82,10 @@ async function authorizeBookingPiiAccess(c, input) {
76
82
  reason: "insufficient_scope",
77
83
  metadata: { actor: actor ?? null },
78
84
  });
79
- return { allowed: false, response: c.json({ error: "Forbidden" }, 403) };
85
+ return {
86
+ allowed: false,
87
+ response: handleApiError(new ForbiddenApiError(), c),
88
+ };
80
89
  }
81
90
  return { allowed: true };
82
91
  }
@@ -89,6 +98,33 @@ function handleKmsConfigError(c, error) {
89
98
  }
90
99
  return c.json({ error: "Booking PII encryption is not configured" }, 500);
91
100
  }
101
+ function getRouteRuntime(c) {
102
+ try {
103
+ return (c.var.container?.resolve(BOOKING_ROUTE_RUNTIME_CONTAINER_KEY) ??
104
+ buildBookingRouteRuntime(c.env));
105
+ }
106
+ catch {
107
+ return buildBookingRouteRuntime(c.env);
108
+ }
109
+ }
110
+ function createAuditedBookingPiiService(c, bookingId) {
111
+ const runtime = getRouteRuntime(c);
112
+ return createBookingPiiService({
113
+ kms: runtime.getKmsProvider(),
114
+ onAudit: async (event) => {
115
+ await logBookingPiiAccess(c, {
116
+ bookingId,
117
+ participantId: event.participantId,
118
+ action: event.action === "encrypt"
119
+ ? "update"
120
+ : event.action === "decrypt"
121
+ ? "read"
122
+ : event.action,
123
+ outcome: "allowed",
124
+ });
125
+ },
126
+ });
127
+ }
92
128
  // ==========================================================================
93
129
  // Bookings — method-chained for Hono RPC type inference
94
130
  // ==========================================================================
@@ -98,12 +134,12 @@ export const bookingRoutes = new Hono()
98
134
  // ==========================================================================
99
135
  // 1. GET / — List bookings
100
136
  .get("/", async (c) => {
101
- const query = bookingListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
137
+ const query = parseQuery(c, bookingListQuerySchema);
102
138
  return c.json(await bookingsService.listBookings(c.get("db"), query));
103
139
  })
104
140
  // 1a. GET /overview — Internal/admin booking overview lookup
105
141
  .get("/overview", async (c) => {
106
- const overview = await publicBookingsService.getOverviewByLookup(c.get("db"), internalBookingOverviewLookupQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams)));
142
+ const overview = await publicBookingsService.getOverviewByLookup(c.get("db"), parseQuery(c, internalBookingOverviewLookupQuerySchema));
107
143
  if (!overview) {
108
144
  return c.json({ error: "Booking overview not found" }, 404);
109
145
  }
@@ -119,7 +155,7 @@ export const bookingRoutes = new Hono()
119
155
  })
120
156
  // 3. POST /reserve — Reserve inventory and create on-hold booking
121
157
  .post("/reserve", async (c) => {
122
- const result = await bookingsService.reserveBooking(c.get("db"), reserveBookingSchema.parse(await c.req.json()), c.get("userId"));
158
+ const result = await bookingsService.reserveBooking(c.get("db"), await parseJsonBody(c, reserveBookingSchema), c.get("userId"));
123
159
  if ("booking" in result) {
124
160
  return c.json({ data: result.booking }, 201);
125
161
  }
@@ -139,7 +175,7 @@ export const bookingRoutes = new Hono()
139
175
  })
140
176
  // 3a. POST /from-product — Create booking draft from product definition
141
177
  .post("/from-product", async (c) => {
142
- const row = await bookingsService.createBookingFromProduct(c.get("db"), convertProductSchema.parse(await c.req.json()), c.get("userId"));
178
+ const row = await bookingsService.createBookingFromProduct(c.get("db"), await parseJsonBody(c, convertProductSchema), c.get("userId"));
143
179
  if (!row) {
144
180
  return c.json({ error: "Product or option not found" }, 404);
145
181
  }
@@ -147,7 +183,7 @@ export const bookingRoutes = new Hono()
147
183
  })
148
184
  // 3b. POST /from-offer/:offerId/reserve — Reserve booking from transaction offer
149
185
  .post("/from-offer/:offerId/reserve", async (c) => {
150
- const result = await bookingsService.reserveBookingFromOffer(c.get("db"), c.req.param("offerId"), reserveBookingFromTransactionSchema.parse(await c.req.json()), c.get("userId"));
186
+ const result = await bookingsService.reserveBookingFromOffer(c.get("db"), c.req.param("offerId"), await parseJsonBody(c, reserveBookingFromTransactionSchema), c.get("userId"));
151
187
  if (result.status === "not_found") {
152
188
  return c.json({ error: "Offer not found" }, 404);
153
189
  }
@@ -170,7 +206,7 @@ export const bookingRoutes = new Hono()
170
206
  })
171
207
  // 3c. POST /from-order/:orderId/reserve — Reserve booking from transaction order
172
208
  .post("/from-order/:orderId/reserve", async (c) => {
173
- const result = await bookingsService.reserveBookingFromOrder(c.get("db"), c.req.param("orderId"), reserveBookingFromTransactionSchema.parse(await c.req.json()), c.get("userId"));
209
+ const result = await bookingsService.reserveBookingFromOrder(c.get("db"), c.req.param("orderId"), await parseJsonBody(c, reserveBookingFromTransactionSchema), c.get("userId"));
174
210
  if (result.status === "not_found") {
175
211
  return c.json({ error: "Order not found" }, 404);
176
212
  }
@@ -193,21 +229,27 @@ export const bookingRoutes = new Hono()
193
229
  })
194
230
  // 4. POST / — Create booking (manual/backoffice only)
195
231
  .post("/", async (c) => {
196
- const payload = await c.req.json();
197
- const parsed = createBookingSchema.safeParse(payload);
198
- if (!parsed.success) {
232
+ try {
199
233
  return c.json({
200
- error: parsed.error.issues[0]?.message ?? "Invalid booking create payload",
201
- details: parsed.error.flatten(),
202
- }, 400);
234
+ data: await bookingsService.createBooking(c.get("db"), await parseJsonBody(c, createBookingSchema, {
235
+ invalidBodyMessage: "Invalid booking create payload",
236
+ }), c.get("userId")),
237
+ }, 201);
238
+ }
239
+ catch (error) {
240
+ const validationError = normalizeValidationError(error);
241
+ if (validationError?.status === 400) {
242
+ return c.json({
243
+ error: validationError.message,
244
+ details: validationError.details?.fields ?? validationError.details,
245
+ }, 400);
246
+ }
247
+ throw error;
203
248
  }
204
- return c.json({
205
- data: await bookingsService.createBooking(c.get("db"), parsed.data, c.get("userId")),
206
- }, 201);
207
249
  })
208
250
  // 5. PATCH /:id — Update booking
209
251
  .patch("/:id", async (c) => {
210
- const row = await bookingsService.updateBooking(c.get("db"), c.req.param("id"), updateBookingSchema.parse(await c.req.json()));
252
+ const row = await bookingsService.updateBooking(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateBookingSchema));
211
253
  if (!row) {
212
254
  return c.json({ error: "Booking not found" }, 404);
213
255
  }
@@ -226,7 +268,7 @@ export const bookingRoutes = new Hono()
226
268
  // ==========================================================================
227
269
  // 7. PATCH /:id/status — Change booking status
228
270
  .patch("/:id/status", async (c) => {
229
- const result = await bookingsService.updateBookingStatus(c.get("db"), c.req.param("id"), updateBookingStatusSchema.parse(await c.req.json()), c.get("userId"));
271
+ const result = await bookingsService.updateBookingStatus(c.get("db"), c.req.param("id"), await parseJsonBody(c, updateBookingStatusSchema), c.get("userId"));
230
272
  if (result.status === "not_found") {
231
273
  return c.json({ error: "Booking not found" }, 404);
232
274
  }
@@ -240,7 +282,7 @@ export const bookingRoutes = new Hono()
240
282
  })
241
283
  // 8. POST /:id/confirm — Confirm an on-hold booking
242
284
  .post("/:id/confirm", async (c) => {
243
- const result = await bookingsService.confirmBooking(c.get("db"), c.req.param("id"), confirmBookingSchema.parse(await c.req.json()), c.get("userId"));
285
+ const result = await bookingsService.confirmBooking(c.get("db"), c.req.param("id"), await parseJsonBody(c, confirmBookingSchema), c.get("userId"));
244
286
  if (result.status === "not_found") {
245
287
  return c.json({ error: "Booking not found" }, 404);
246
288
  }
@@ -257,7 +299,7 @@ export const bookingRoutes = new Hono()
257
299
  })
258
300
  // 9. POST /:id/extend-hold — Extend booking hold expiry
259
301
  .post("/:id/extend-hold", async (c) => {
260
- const result = await bookingsService.extendBookingHold(c.get("db"), c.req.param("id"), extendBookingHoldSchema.parse(await c.req.json()), c.get("userId"));
302
+ const result = await bookingsService.extendBookingHold(c.get("db"), c.req.param("id"), await parseJsonBody(c, extendBookingHoldSchema), c.get("userId"));
261
303
  if (result.status === "not_found") {
262
304
  return c.json({ error: "Booking not found" }, 404);
263
305
  }
@@ -274,7 +316,7 @@ export const bookingRoutes = new Hono()
274
316
  })
275
317
  // 10. POST /:id/expire — Expire an on-hold booking
276
318
  .post("/:id/expire", async (c) => {
277
- const result = await bookingsService.expireBooking(c.get("db"), c.req.param("id"), expireBookingSchema.parse(await c.req.json()), c.get("userId"));
319
+ const result = await bookingsService.expireBooking(c.get("db"), c.req.param("id"), await parseJsonBody(c, expireBookingSchema), c.get("userId"));
278
320
  if (result.status === "not_found") {
279
321
  return c.json({ error: "Booking not found" }, 404);
280
322
  }
@@ -288,11 +330,11 @@ export const bookingRoutes = new Hono()
288
330
  })
289
331
  // 10b. POST /expire-stale — Expire all stale on-hold bookings up to a cutoff
290
332
  .post("/expire-stale", async (c) => {
291
- return c.json(await bookingsService.expireStaleBookings(c.get("db"), expireStaleBookingsSchema.parse(await c.req.json()), c.get("userId")));
333
+ return c.json(await bookingsService.expireStaleBookings(c.get("db"), await parseJsonBody(c, expireStaleBookingsSchema), c.get("userId")));
292
334
  })
293
335
  // 11. POST /:id/cancel — Cancel a booking and release allocations
294
336
  .post("/:id/cancel", async (c) => {
295
- const result = await bookingsService.cancelBooking(c.get("db"), c.req.param("id"), cancelBookingSchema.parse(await c.req.json()), c.get("userId"));
337
+ const result = await bookingsService.cancelBooking(c.get("db"), c.req.param("id"), await parseJsonBody(c, cancelBookingSchema), c.get("userId"));
296
338
  if (result.status === "not_found") {
297
339
  return c.json({ error: "Booking not found" }, 404);
298
340
  }
@@ -337,21 +379,7 @@ export const bookingRoutes = new Hono()
337
379
  return c.json({ error: "Participant not found" }, 404);
338
380
  }
339
381
  try {
340
- const pii = createBookingPiiService({
341
- kms: createKmsProviderFromEnv(getRuntimeEnv(c)),
342
- onAudit: async (event) => {
343
- await logBookingPiiAccess(c, {
344
- bookingId: participant.bookingId,
345
- participantId: event.participantId,
346
- action: event.action === "encrypt"
347
- ? "update"
348
- : event.action === "decrypt"
349
- ? "read"
350
- : event.action,
351
- outcome: "allowed",
352
- });
353
- },
354
- });
382
+ const pii = createAuditedBookingPiiService(c, participant.bookingId);
355
383
  const details = await pii.getParticipantTravelDetails(c.get("db"), participant.id, c.get("userId"));
356
384
  if (!details) {
357
385
  await logBookingPiiAccess(c, {
@@ -371,7 +399,7 @@ export const bookingRoutes = new Hono()
371
399
  })
372
400
  // 9. POST /:id/participants — Add participant
373
401
  .post("/:id/participants", async (c) => {
374
- const row = await bookingsService.createParticipant(c.get("db"), c.req.param("id"), insertParticipantSchema.parse(await c.req.json()), c.get("userId"));
402
+ const row = await bookingsService.createParticipant(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertParticipantSchema), c.get("userId"));
375
403
  if (!row) {
376
404
  return c.json({ error: "Booking not found" }, 404);
377
405
  }
@@ -399,22 +427,8 @@ export const bookingRoutes = new Hono()
399
427
  return c.json({ error: "Participant not found" }, 404);
400
428
  }
401
429
  try {
402
- const pii = createBookingPiiService({
403
- kms: createKmsProviderFromEnv(getRuntimeEnv(c)),
404
- onAudit: async (event) => {
405
- await logBookingPiiAccess(c, {
406
- bookingId: participant.bookingId,
407
- participantId: event.participantId,
408
- action: event.action === "encrypt"
409
- ? "update"
410
- : event.action === "decrypt"
411
- ? "read"
412
- : event.action,
413
- outcome: "allowed",
414
- });
415
- },
416
- });
417
- const row = await pii.upsertParticipantTravelDetails(c.get("db"), participant.id, upsertParticipantTravelDetailsSchema.parse(await c.req.json()), c.get("userId"));
430
+ const pii = createAuditedBookingPiiService(c, participant.bookingId);
431
+ const row = await pii.upsertParticipantTravelDetails(c.get("db"), participant.id, await parseJsonBody(c, upsertParticipantTravelDetailsSchema), c.get("userId"));
418
432
  if (!row) {
419
433
  return c.json({ error: "Participant not found" }, 404);
420
434
  }
@@ -426,7 +440,7 @@ export const bookingRoutes = new Hono()
426
440
  })
427
441
  // 10. PATCH /:id/participants/:participantId — Update participant
428
442
  .patch("/:id/participants/:participantId", async (c) => {
429
- const row = await bookingsService.updateParticipant(c.get("db"), c.req.param("participantId"), updateParticipantSchema.parse(await c.req.json()));
443
+ const row = await bookingsService.updateParticipant(c.get("db"), c.req.param("participantId"), await parseJsonBody(c, updateParticipantSchema));
430
444
  if (!row) {
431
445
  return c.json({ error: "Participant not found" }, 404);
432
446
  }
@@ -454,21 +468,7 @@ export const bookingRoutes = new Hono()
454
468
  return c.json({ error: "Participant not found" }, 404);
455
469
  }
456
470
  try {
457
- const pii = createBookingPiiService({
458
- kms: createKmsProviderFromEnv(getRuntimeEnv(c)),
459
- onAudit: async (event) => {
460
- await logBookingPiiAccess(c, {
461
- bookingId: participant.bookingId,
462
- participantId: event.participantId,
463
- action: event.action === "encrypt"
464
- ? "update"
465
- : event.action === "decrypt"
466
- ? "read"
467
- : event.action,
468
- outcome: "allowed",
469
- });
470
- },
471
- });
471
+ const pii = createAuditedBookingPiiService(c, participant.bookingId);
472
472
  const row = await pii.deleteParticipantTravelDetails(c.get("db"), participant.id, c.get("userId"));
473
473
  if (!row) {
474
474
  return c.json({ error: "Participant travel details not found" }, 404);
@@ -496,7 +496,7 @@ export const bookingRoutes = new Hono()
496
496
  })
497
497
  // 13. POST /:id/passengers — Add passenger
498
498
  .post("/:id/passengers", async (c) => {
499
- const row = await bookingsService.createPassenger(c.get("db"), c.req.param("id"), insertPassengerSchema.parse(await c.req.json()), c.get("userId"));
499
+ const row = await bookingsService.createPassenger(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertPassengerSchema), c.get("userId"));
500
500
  if (!row) {
501
501
  return c.json({ error: "Booking not found" }, 404);
502
502
  }
@@ -504,7 +504,7 @@ export const bookingRoutes = new Hono()
504
504
  })
505
505
  // 14. PATCH /:id/passengers/:passengerId — Update passenger
506
506
  .patch("/:id/passengers/:passengerId", async (c) => {
507
- const row = await bookingsService.updatePassenger(c.get("db"), c.req.param("passengerId"), updatePassengerSchema.parse(await c.req.json()));
507
+ const row = await bookingsService.updatePassenger(c.get("db"), c.req.param("passengerId"), await parseJsonBody(c, updatePassengerSchema));
508
508
  if (!row) {
509
509
  return c.json({ error: "Passenger not found" }, 404);
510
510
  }
@@ -527,7 +527,7 @@ export const bookingRoutes = new Hono()
527
527
  })
528
528
  // 17. POST /:id/items — Add booking item
529
529
  .post("/:id/items", async (c) => {
530
- const row = await bookingsService.createItem(c.get("db"), c.req.param("id"), insertBookingItemSchema.parse(await c.req.json()), c.get("userId"));
530
+ const row = await bookingsService.createItem(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertBookingItemSchema), c.get("userId"));
531
531
  if (!row) {
532
532
  return c.json({ error: "Booking not found" }, 404);
533
533
  }
@@ -535,7 +535,7 @@ export const bookingRoutes = new Hono()
535
535
  })
536
536
  // 18. PATCH /:id/items/:itemId — Update booking item
537
537
  .patch("/:id/items/:itemId", async (c) => {
538
- const row = await bookingsService.updateItem(c.get("db"), c.req.param("itemId"), updateBookingItemSchema.parse(await c.req.json()));
538
+ const row = await bookingsService.updateItem(c.get("db"), c.req.param("itemId"), await parseJsonBody(c, updateBookingItemSchema));
539
539
  if (!row) {
540
540
  return c.json({ error: "Booking item not found" }, 404);
541
541
  }
@@ -557,7 +557,7 @@ export const bookingRoutes = new Hono()
557
557
  })
558
558
  // 21. POST /:id/items/:itemId/participants — Link participant to item
559
559
  .post("/:id/items/:itemId/participants", async (c) => {
560
- const row = await bookingsService.addItemParticipant(c.get("db"), c.req.param("itemId"), insertBookingItemParticipantSchema.parse(await c.req.json()));
560
+ const row = await bookingsService.addItemParticipant(c.get("db"), c.req.param("itemId"), await parseJsonBody(c, insertBookingItemParticipantSchema));
561
561
  if (!row) {
562
562
  return c.json({ error: "Booking item or participant not found" }, 404);
563
563
  }
@@ -580,14 +580,14 @@ export const bookingRoutes = new Hono()
580
580
  });
581
581
  })
582
582
  .post("/:id/supplier-statuses", async (c) => {
583
- const row = await bookingsService.createSupplierStatus(c.get("db"), c.req.param("id"), insertSupplierStatusSchema.parse(await c.req.json()), c.get("userId"));
583
+ const row = await bookingsService.createSupplierStatus(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertSupplierStatusSchema), c.get("userId"));
584
584
  if (!row) {
585
585
  return c.json({ error: "Booking not found" }, 404);
586
586
  }
587
587
  return c.json({ data: row }, 201);
588
588
  })
589
589
  .patch("/:id/supplier-statuses/:statusId", async (c) => {
590
- const row = await bookingsService.updateSupplierStatus(c.get("db"), c.req.param("id"), c.req.param("statusId"), updateSupplierStatusSchema.parse(await c.req.json()), c.get("userId"));
590
+ const row = await bookingsService.updateSupplierStatus(c.get("db"), c.req.param("id"), c.req.param("statusId"), await parseJsonBody(c, updateSupplierStatusSchema), c.get("userId"));
591
591
  if (!row) {
592
592
  return c.json({ error: "Supplier status not found" }, 404);
593
593
  }
@@ -600,14 +600,14 @@ export const bookingRoutes = new Hono()
600
600
  return c.json({ data: await bookingsService.listFulfillments(c.get("db"), c.req.param("id")) });
601
601
  })
602
602
  .post("/:id/fulfillments", async (c) => {
603
- const row = await bookingsService.issueFulfillment(c.get("db"), c.req.param("id"), insertBookingFulfillmentSchema.parse(await c.req.json()), c.get("userId"));
603
+ const row = await bookingsService.issueFulfillment(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertBookingFulfillmentSchema), c.get("userId"));
604
604
  if (!row) {
605
605
  return c.json({ error: "Booking, item, or participant not found" }, 404);
606
606
  }
607
607
  return c.json({ data: row }, 201);
608
608
  })
609
609
  .patch("/:id/fulfillments/:fulfillmentId", async (c) => {
610
- const row = await bookingsService.updateFulfillment(c.get("db"), c.req.param("id"), c.req.param("fulfillmentId"), updateBookingFulfillmentSchema.parse(await c.req.json()), c.get("userId"));
610
+ const row = await bookingsService.updateFulfillment(c.get("db"), c.req.param("id"), c.req.param("fulfillmentId"), await parseJsonBody(c, updateBookingFulfillmentSchema), c.get("userId"));
611
611
  if (!row) {
612
612
  return c.json({ error: "Fulfillment, item, or participant not found" }, 404);
613
613
  }
@@ -622,7 +622,7 @@ export const bookingRoutes = new Hono()
622
622
  });
623
623
  })
624
624
  .post("/:id/redemptions", async (c) => {
625
- const row = await bookingsService.recordRedemption(c.get("db"), c.req.param("id"), recordBookingRedemptionSchema.parse(await c.req.json()), c.get("userId"));
625
+ const row = await bookingsService.recordRedemption(c.get("db"), c.req.param("id"), await parseJsonBody(c, recordBookingRedemptionSchema), c.get("userId"));
626
626
  if (!row) {
627
627
  return c.json({ error: "Booking, item, or participant not found" }, 404);
628
628
  }
@@ -649,11 +649,8 @@ export const bookingRoutes = new Hono()
649
649
  })
650
650
  // 28. POST /:id/notes — Add note
651
651
  .post("/:id/notes", async (c) => {
652
- const userId = c.get("userId");
653
- if (!userId) {
654
- return c.json({ error: "User ID required to create notes" }, 400);
655
- }
656
- const row = await bookingsService.createNote(c.get("db"), c.req.param("id"), userId, insertBookingNoteSchema.parse(await c.req.json()));
652
+ const userId = requireUserId(c);
653
+ const row = await bookingsService.createNote(c.get("db"), c.req.param("id"), userId, await parseJsonBody(c, insertBookingNoteSchema));
657
654
  if (!row) {
658
655
  return c.json({ error: "Booking not found" }, 404);
659
656
  }
@@ -668,7 +665,7 @@ export const bookingRoutes = new Hono()
668
665
  })
669
666
  // 30. POST /:id/documents — Add document to booking
670
667
  .post("/:id/documents", async (c) => {
671
- const row = await bookingsService.createDocument(c.get("db"), c.req.param("id"), insertBookingDocumentSchema.parse(await c.req.json()));
668
+ const row = await bookingsService.createDocument(c.get("db"), c.req.param("id"), await parseJsonBody(c, insertBookingDocumentSchema));
672
669
  if (!row) {
673
670
  return c.json({ error: "Booking not found" }, 404);
674
671
  }
@@ -92,7 +92,7 @@ export declare const bookings: import("drizzle-orm/pg-core").PgTableWithColumns<
92
92
  tableName: "bookings";
93
93
  dataType: "string";
94
94
  columnType: "PgEnumColumn";
95
- data: "internal" | "direct" | "manual" | "affiliate" | "ota" | "reseller" | "api_partner";
95
+ data: "internal" | "reseller" | "direct" | "manual" | "affiliate" | "ota" | "api_partner";
96
96
  driverParam: string;
97
97
  notNull: true;
98
98
  hasDefault: true;
@@ -75,7 +75,7 @@ export declare const bookingItems: import("drizzle-orm/pg-core").PgTableWithColu
75
75
  tableName: "booking_items";
76
76
  dataType: "string";
77
77
  columnType: "PgEnumColumn";
78
- data: "other" | "unit" | "extra" | "service" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
78
+ data: "service" | "other" | "unit" | "extra" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
79
79
  driverParam: string;
80
80
  notNull: true;
81
81
  hasDefault: true;
@@ -611,7 +611,7 @@ export declare const bookingAllocations: import("drizzle-orm/pg-core").PgTableWi
611
611
  tableName: "booking_allocations";
612
612
  dataType: "string";
613
613
  columnType: "PgEnumColumn";
614
- data: "unit" | "pickup" | "resource";
614
+ data: "resource" | "unit" | "pickup";
615
615
  driverParam: string;
616
616
  notNull: true;
617
617
  hasDefault: true;
@@ -39,7 +39,7 @@ export declare const publicBookingsService: {
39
39
  id: string;
40
40
  title: string;
41
41
  description: string | null;
42
- itemType: "other" | "unit" | "extra" | "service" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
42
+ itemType: "service" | "other" | "unit" | "extra" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
43
43
  status: "cancelled" | "draft" | "on_hold" | "confirmed" | "expired" | "fulfilled";
44
44
  serviceDate: string | null;
45
45
  startsAt: string | null;
@@ -72,7 +72,7 @@ export declare const publicBookingsService: {
72
72
  pricingCategoryId: string | null;
73
73
  availabilitySlotId: string | null;
74
74
  quantity: number;
75
- allocationType: "unit" | "pickup" | "resource";
75
+ allocationType: "resource" | "unit" | "pickup";
76
76
  status: "cancelled" | "confirmed" | "expired" | "fulfilled" | "held" | "released";
77
77
  holdExpiresAt: string | null;
78
78
  confirmedAt: string | null;
@@ -132,7 +132,7 @@ export declare const publicBookingsService: {
132
132
  id: string;
133
133
  title: string;
134
134
  description: string | null;
135
- itemType: "other" | "unit" | "extra" | "service" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
135
+ itemType: "service" | "other" | "unit" | "extra" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
136
136
  status: "cancelled" | "draft" | "on_hold" | "confirmed" | "expired" | "fulfilled";
137
137
  serviceDate: string | null;
138
138
  startsAt: string | null;
@@ -165,7 +165,7 @@ export declare const publicBookingsService: {
165
165
  pricingCategoryId: string | null;
166
166
  availabilitySlotId: string | null;
167
167
  quantity: number;
168
- allocationType: "unit" | "pickup" | "resource";
168
+ allocationType: "resource" | "unit" | "pickup";
169
169
  status: "cancelled" | "confirmed" | "expired" | "fulfilled" | "held" | "released";
170
170
  holdExpiresAt: string | null;
171
171
  confirmedAt: string | null;
@@ -254,7 +254,7 @@ export declare const publicBookingsService: {
254
254
  id: string;
255
255
  title: string;
256
256
  description: string | null;
257
- itemType: "other" | "unit" | "extra" | "service" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
257
+ itemType: "service" | "other" | "unit" | "extra" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
258
258
  status: "cancelled" | "draft" | "on_hold" | "confirmed" | "expired" | "fulfilled";
259
259
  serviceDate: string | null;
260
260
  startsAt: string | null;
@@ -287,7 +287,7 @@ export declare const publicBookingsService: {
287
287
  pricingCategoryId: string | null;
288
288
  availabilitySlotId: string | null;
289
289
  quantity: number;
290
- allocationType: "unit" | "pickup" | "resource";
290
+ allocationType: "resource" | "unit" | "pickup";
291
291
  status: "cancelled" | "confirmed" | "expired" | "fulfilled" | "held" | "released";
292
292
  holdExpiresAt: string | null;
293
293
  confirmedAt: string | null;
@@ -388,7 +388,7 @@ export declare const publicBookingsService: {
388
388
  id: string;
389
389
  title: string;
390
390
  description: string | null;
391
- itemType: "other" | "unit" | "extra" | "service" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
391
+ itemType: "service" | "other" | "unit" | "extra" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
392
392
  status: "cancelled" | "draft" | "on_hold" | "confirmed" | "expired" | "fulfilled";
393
393
  serviceDate: string | null;
394
394
  startsAt: string | null;
@@ -421,7 +421,7 @@ export declare const publicBookingsService: {
421
421
  pricingCategoryId: string | null;
422
422
  availabilitySlotId: string | null;
423
423
  quantity: number;
424
- allocationType: "unit" | "pickup" | "resource";
424
+ allocationType: "resource" | "unit" | "pickup";
425
425
  status: "cancelled" | "confirmed" | "expired" | "fulfilled" | "held" | "released";
426
426
  holdExpiresAt: string | null;
427
427
  confirmedAt: string | null;
@@ -485,7 +485,7 @@ export declare const publicBookingsService: {
485
485
  id: string;
486
486
  title: string;
487
487
  description: string | null;
488
- itemType: "other" | "unit" | "extra" | "service" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
488
+ itemType: "service" | "other" | "unit" | "extra" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
489
489
  status: "cancelled" | "draft" | "on_hold" | "confirmed" | "expired" | "fulfilled";
490
490
  serviceDate: string | null;
491
491
  startsAt: string | null;
@@ -518,7 +518,7 @@ export declare const publicBookingsService: {
518
518
  pricingCategoryId: string | null;
519
519
  availabilitySlotId: string | null;
520
520
  quantity: number;
521
- allocationType: "unit" | "pickup" | "resource";
521
+ allocationType: "resource" | "unit" | "pickup";
522
522
  status: "cancelled" | "confirmed" | "expired" | "fulfilled" | "held" | "released";
523
523
  holdExpiresAt: string | null;
524
524
  confirmedAt: string | null;
@@ -582,7 +582,7 @@ export declare const publicBookingsService: {
582
582
  id: string;
583
583
  title: string;
584
584
  description: string | null;
585
- itemType: "other" | "unit" | "extra" | "service" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
585
+ itemType: "service" | "other" | "unit" | "extra" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
586
586
  status: "cancelled" | "draft" | "on_hold" | "confirmed" | "expired" | "fulfilled";
587
587
  serviceDate: string | null;
588
588
  startsAt: string | null;
@@ -615,7 +615,7 @@ export declare const publicBookingsService: {
615
615
  pricingCategoryId: string | null;
616
616
  availabilitySlotId: string | null;
617
617
  quantity: number;
618
- allocationType: "unit" | "pickup" | "resource";
618
+ allocationType: "resource" | "unit" | "pickup";
619
619
  status: "cancelled" | "confirmed" | "expired" | "fulfilled" | "held" | "released";
620
620
  holdExpiresAt: string | null;
621
621
  confirmedAt: string | null;
@@ -664,7 +664,7 @@ export declare const publicBookingsService: {
664
664
  id: string;
665
665
  title: string;
666
666
  description: string | null;
667
- itemType: "other" | "unit" | "extra" | "service" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
667
+ itemType: "service" | "other" | "unit" | "extra" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
668
668
  status: "cancelled" | "draft" | "on_hold" | "confirmed" | "expired" | "fulfilled";
669
669
  serviceDate: string | null;
670
670
  startsAt: string | null;
@@ -728,7 +728,7 @@ export declare const publicBookingsService: {
728
728
  id: string;
729
729
  title: string;
730
730
  description: string | null;
731
- itemType: "other" | "unit" | "extra" | "service" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
731
+ itemType: "service" | "other" | "unit" | "extra" | "fee" | "tax" | "discount" | "adjustment" | "accommodation" | "transport";
732
732
  status: "cancelled" | "draft" | "on_hold" | "confirmed" | "expired" | "fulfilled";
733
733
  serviceDate: string | null;
734
734
  startsAt: string | null;