@voyantjs/octo 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAmBjE,KAAK,GAAG,GAAG;IACT,SAAS,EAAE;QACT,EAAE,EAAE,kBAAkB,CAAA;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAA;CACF,CAAA;AAED,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BA6InB,CAAA;AAEJ,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAA"}
package/dist/routes.js ADDED
@@ -0,0 +1,121 @@
1
+ import { cancelBookingSchema, confirmBookingSchema, expireBookingSchema, extendBookingHoldSchema, recordBookingRedemptionSchema, reserveBookingSchema, } from "@voyantjs/bookings";
2
+ import { Hono } from "hono";
3
+ import { octoService } from "./service.js";
4
+ import { octoAvailabilityCalendarQuerySchema, octoAvailabilityListQuerySchema, octoBookingListQuerySchema, octoProductListQuerySchema, } from "./validation.js";
5
+ export const octoRoutes = new Hono()
6
+ .get("/products", async (c) => {
7
+ const query = octoProductListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
8
+ return c.json(await octoService.listProjectedProducts(c.get("db"), query));
9
+ })
10
+ .get("/availability", async (c) => {
11
+ const query = octoAvailabilityListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
12
+ return c.json(await octoService.listProjectedAvailability(c.get("db"), query));
13
+ })
14
+ .get("/products/:id", async (c) => {
15
+ const row = await octoService.getProjectedProductById(c.get("db"), c.req.param("id"));
16
+ if (!row)
17
+ return c.json({ error: "Product not found" }, 404);
18
+ return c.json({ data: row });
19
+ })
20
+ .get("/products/:id/availability", async (c) => {
21
+ const query = octoAvailabilityListQuerySchema.parse({
22
+ ...Object.fromEntries(new URL(c.req.url).searchParams),
23
+ productId: c.req.param("id"),
24
+ });
25
+ return c.json(await octoService.listProjectedAvailability(c.get("db"), query));
26
+ })
27
+ .get("/products/:id/calendar", async (c) => {
28
+ const query = octoAvailabilityCalendarQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
29
+ return c.json(await octoService.getProjectedAvailabilityCalendar(c.get("db"), c.req.param("id"), query));
30
+ })
31
+ .get("/availability/:id", async (c) => {
32
+ const row = await octoService.getProjectedAvailabilityById(c.get("db"), c.req.param("id"));
33
+ if (!row)
34
+ return c.json({ error: "Availability not found" }, 404);
35
+ return c.json({ data: row });
36
+ })
37
+ .post("/bookings", async (c) => {
38
+ const result = await octoService.reserveProjectedBooking(c.get("db"), reserveBookingSchema.parse(await c.req.json()), c.get("userId"));
39
+ if ("booking" in result && result.booking) {
40
+ return c.json({ data: result.booking }, 201);
41
+ }
42
+ if (result.status === "slot_not_found")
43
+ return c.json({ error: "Availability slot not found" }, 404);
44
+ if (result.status === "insufficient_capacity")
45
+ return c.json({ error: "Insufficient slot capacity" }, 409);
46
+ if (result.status === "slot_unavailable")
47
+ return c.json({ error: "Availability slot is not bookable" }, 409);
48
+ if (result.status === "slot_product_mismatch" || result.status === "slot_option_mismatch") {
49
+ return c.json({ error: "Reservation item does not match availability slot" }, 409);
50
+ }
51
+ return c.json({ error: "Unable to reserve booking" }, 400);
52
+ })
53
+ .get("/bookings", async (c) => {
54
+ const query = octoBookingListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
55
+ return c.json(await octoService.listProjectedBookings(c.get("db"), query));
56
+ })
57
+ .get("/bookings/:id", async (c) => {
58
+ const row = await octoService.getProjectedBookingById(c.get("db"), c.req.param("id"));
59
+ if (!row)
60
+ return c.json({ error: "Booking not found" }, 404);
61
+ return c.json({ data: row });
62
+ })
63
+ .post("/bookings/:id/confirm", async (c) => {
64
+ const result = await octoService.confirmProjectedBooking(c.get("db"), c.req.param("id"), confirmBookingSchema.parse(await c.req.json()), c.get("userId"));
65
+ if ("booking" in result && result.booking)
66
+ return c.json({ data: result.booking });
67
+ if (result.status === "not_found")
68
+ return c.json({ error: "Booking not found" }, 404);
69
+ if (result.status === "invalid_transition")
70
+ return c.json({ error: "Invalid booking status transition" }, 409);
71
+ if (result.status === "hold_expired")
72
+ return c.json({ error: "Booking hold has expired" }, 409);
73
+ return c.json({ error: "Unable to confirm booking" }, 400);
74
+ })
75
+ .post("/bookings/:id/extend-hold", async (c) => {
76
+ const result = await octoService.extendProjectedBookingHold(c.get("db"), c.req.param("id"), extendBookingHoldSchema.parse(await c.req.json()), c.get("userId"));
77
+ if ("booking" in result && result.booking)
78
+ return c.json({ data: result.booking });
79
+ if (result.status === "not_found")
80
+ return c.json({ error: "Booking not found" }, 404);
81
+ if (result.status === "invalid_transition")
82
+ return c.json({ error: "Invalid booking status transition" }, 409);
83
+ if (result.status === "hold_expired")
84
+ return c.json({ error: "Booking hold has expired" }, 409);
85
+ return c.json({ error: "Unable to extend booking hold" }, 400);
86
+ })
87
+ .post("/bookings/:id/expire", async (c) => {
88
+ const result = await octoService.expireProjectedBooking(c.get("db"), c.req.param("id"), expireBookingSchema.parse(await c.req.json()), c.get("userId"));
89
+ if ("booking" in result && result.booking)
90
+ return c.json({ data: result.booking });
91
+ if (result.status === "not_found")
92
+ return c.json({ error: "Booking not found" }, 404);
93
+ if (result.status === "invalid_transition")
94
+ return c.json({ error: "Invalid booking status transition" }, 409);
95
+ return c.json({ error: "Unable to expire booking" }, 400);
96
+ })
97
+ .post("/bookings/:id/cancel", async (c) => {
98
+ const result = await octoService.cancelProjectedBooking(c.get("db"), c.req.param("id"), cancelBookingSchema.parse(await c.req.json()), c.get("userId"));
99
+ if ("booking" in result && result.booking)
100
+ return c.json({ data: result.booking });
101
+ if (result.status === "not_found")
102
+ return c.json({ error: "Booking not found" }, 404);
103
+ if (result.status === "invalid_transition")
104
+ return c.json({ error: "Invalid booking status transition" }, 409);
105
+ return c.json({ error: "Unable to cancel booking" }, 400);
106
+ })
107
+ .get("/bookings/:id/redemptions", async (c) => {
108
+ const booking = await octoService.getProjectedBookingById(c.get("db"), c.req.param("id"));
109
+ if (!booking)
110
+ return c.json({ error: "Booking not found" }, 404);
111
+ return c.json({
112
+ data: await octoService.listProjectedRedemptions(c.get("db"), c.req.param("id")),
113
+ });
114
+ })
115
+ .post("/bookings/:id/redeem", async (c) => {
116
+ const result = await octoService.recordProjectedRedemption(c.get("db"), c.req.param("id"), recordBookingRedemptionSchema.parse(await c.req.json()), c.get("userId"));
117
+ if (!result) {
118
+ return c.json({ error: "Booking, item, or participant not found" }, 404);
119
+ }
120
+ return c.json({ data: result.event, booking: result.booking }, 201);
121
+ });
@@ -0,0 +1,124 @@
1
+ import { availabilitySlots } from "@voyantjs/availability/schema";
2
+ import { bookingsService } from "@voyantjs/bookings";
3
+ import { bookingFulfillments, bookings } from "@voyantjs/bookings/schema";
4
+ import { optionUnits, products } from "@voyantjs/products/schema";
5
+ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
6
+ import type { z } from "zod";
7
+ import type { OctoAvailabilityStatus, OctoAvailabilityType, OctoBookingStatus, OctoProjectedAvailability, OctoProjectedBooking, OctoProjectedProduct, OctoUnitType } from "./types.js";
8
+ import type { octoAvailabilityCalendarQuerySchema, octoAvailabilityListQuerySchema, octoBookingListQuerySchema, octoProductListQuerySchema } from "./validation.js";
9
+ type ProductRow = typeof products.$inferSelect;
10
+ type UnitRow = typeof optionUnits.$inferSelect;
11
+ type SlotRow = typeof availabilitySlots.$inferSelect;
12
+ type BookingRow = typeof bookings.$inferSelect;
13
+ type OctoAvailabilityListQuery = z.infer<typeof octoAvailabilityListQuerySchema>;
14
+ type OctoAvailabilityCalendarQuery = z.infer<typeof octoAvailabilityCalendarQuerySchema>;
15
+ type OctoProductListQuery = z.infer<typeof octoProductListQuerySchema>;
16
+ type OctoBookingListQuery = z.infer<typeof octoBookingListQuerySchema>;
17
+ export declare function inferOctoAvailabilityType(bookingMode: ProductRow["bookingMode"]): OctoAvailabilityType;
18
+ export declare function inferOctoUnitType(unit: Pick<UnitRow, "name" | "code" | "unitType">): OctoUnitType;
19
+ export declare function deriveOctoAvailabilityStatus(slot: Pick<SlotRow, "status" | "unlimited" | "initialPax" | "remainingPax">, capacityMode: ProductRow["capacityMode"] | null | undefined): OctoAvailabilityStatus;
20
+ export declare function mapBookingStatus(status: BookingRow["status"]): OctoBookingStatus;
21
+ export declare function mapBookingArtifact(fulfillment: typeof bookingFulfillments.$inferSelect): {
22
+ fulfillmentId: string;
23
+ bookingItemId: string | null;
24
+ participantId: string | null;
25
+ type: "other" | "voucher" | "ticket" | "pdf" | "qr_code" | "barcode" | "mobile";
26
+ deliveryChannel: "other" | "download" | "email" | "api" | "wallet";
27
+ status: "pending" | "issued" | "reissued" | "revoked" | "failed";
28
+ artifactUrl: string | null;
29
+ downloadUrl: string | null;
30
+ pdfUrl: string | null;
31
+ qrCode: string | null;
32
+ barcode: string | null;
33
+ voucherCode: string | null;
34
+ issuedAt: string | null;
35
+ revokedAt: string | null;
36
+ };
37
+ export declare const octoService: {
38
+ getProjectedProductById(db: PostgresJsDatabase, id: string): Promise<OctoProjectedProduct | null>;
39
+ getProjectedAvailabilityById(db: PostgresJsDatabase, id: string): Promise<OctoProjectedAvailability | null>;
40
+ listProjectedAvailability(db: PostgresJsDatabase, query: OctoAvailabilityListQuery): Promise<{
41
+ data: OctoProjectedAvailability[];
42
+ total: number;
43
+ limit: number;
44
+ offset: number;
45
+ }>;
46
+ getProjectedAvailabilityCalendar(db: PostgresJsDatabase, productId: string, query: OctoAvailabilityCalendarQuery): Promise<{
47
+ data: {
48
+ localDate: string;
49
+ status: OctoAvailabilityStatus;
50
+ vacancies: number | null;
51
+ capacity: number | null;
52
+ availabilityIds: string[];
53
+ }[];
54
+ total: number;
55
+ }>;
56
+ listProjectedProducts(db: PostgresJsDatabase, query: OctoProductListQuery): Promise<{
57
+ data: OctoProjectedProduct[];
58
+ total: number;
59
+ limit: number;
60
+ offset: number;
61
+ }>;
62
+ getProjectedBookingById(db: PostgresJsDatabase, id: string): Promise<OctoProjectedBooking | null>;
63
+ listProjectedBookings(db: PostgresJsDatabase, query: OctoBookingListQuery): Promise<{
64
+ data: OctoProjectedBooking[];
65
+ total: number;
66
+ limit: number;
67
+ offset: number;
68
+ }>;
69
+ reserveProjectedBooking(db: PostgresJsDatabase, data: Parameters<typeof bookingsService.reserveBooking>[1], userId?: string): Promise<{
70
+ status: Exclude<string, "ok">;
71
+ } | {
72
+ status: "ok";
73
+ booking: OctoProjectedBooking | null;
74
+ }>;
75
+ confirmProjectedBooking(db: PostgresJsDatabase, id: string, data: Parameters<typeof bookingsService.confirmBooking>[2], userId?: string): Promise<{
76
+ status: Exclude<string, "ok">;
77
+ } | {
78
+ status: "ok";
79
+ booking: OctoProjectedBooking | null;
80
+ }>;
81
+ extendProjectedBookingHold(db: PostgresJsDatabase, id: string, data: Parameters<typeof bookingsService.extendBookingHold>[2], userId?: string): Promise<{
82
+ status: Exclude<string, "ok">;
83
+ } | {
84
+ status: "ok";
85
+ booking: OctoProjectedBooking | null;
86
+ }>;
87
+ expireProjectedBooking(db: PostgresJsDatabase, id: string, data: Parameters<typeof bookingsService.expireBooking>[2], userId?: string): Promise<{
88
+ status: Exclude<string, "ok">;
89
+ } | {
90
+ status: "ok";
91
+ booking: OctoProjectedBooking | null;
92
+ }>;
93
+ cancelProjectedBooking(db: PostgresJsDatabase, id: string, data: Parameters<typeof bookingsService.cancelBooking>[2], userId?: string): Promise<{
94
+ status: Exclude<string, "ok">;
95
+ } | {
96
+ status: "ok";
97
+ booking: OctoProjectedBooking | null;
98
+ }>;
99
+ listProjectedRedemptions(db: PostgresJsDatabase, bookingId: string): Promise<{
100
+ id: string;
101
+ bookingItemId: string | null;
102
+ participantId: string | null;
103
+ redeemedAt: string;
104
+ redeemedBy: string | null;
105
+ location: string | null;
106
+ method: "other" | "manual" | "api" | "scan";
107
+ metadata: Record<string, unknown> | null;
108
+ }[]>;
109
+ recordProjectedRedemption(db: PostgresJsDatabase, bookingId: string, data: Parameters<typeof bookingsService.recordRedemption>[2], userId?: string): Promise<{
110
+ event: {
111
+ id: string;
112
+ bookingItemId: string | null;
113
+ participantId: string | null;
114
+ redeemedAt: string;
115
+ redeemedBy: string | null;
116
+ location: string | null;
117
+ method: "other" | "manual" | "api" | "scan";
118
+ metadata: Record<string, unknown> | null;
119
+ };
120
+ booking: OctoProjectedBooking | null;
121
+ } | null>;
122
+ };
123
+ export {};
124
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}