@voyantjs/catalog-authoring 0.104.1

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,50 @@
1
+ import type { HonoExtension } from "@voyantjs/hono/module";
2
+ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
3
+ /**
4
+ * Catalog authoring rides on the `products` admin prefix as a HonoExtension,
5
+ * so its routes land at `/v1/admin/products/...` without `packages/products`
6
+ * depending on this package (which would cycle, since this package depends on
7
+ * both products and pricing). Same mechanism as `bookingsSupplierExtension`.
8
+ *
9
+ * POST /v1/admin/products/{id}/duplicate — clone (#1493)
10
+ * POST /v1/admin/products/compose — compose (#1495)
11
+ *
12
+ * Phase 0: routes are mounted but not yet implemented. The builder, clone,
13
+ * validator and compose land in later phases.
14
+ */
15
+ type Env = {
16
+ Variables: {
17
+ db: PostgresJsDatabase;
18
+ userId?: string;
19
+ };
20
+ };
21
+ export declare const catalogAuthoringRoutes: import("hono/hono-base").HonoBase<Env, {
22
+ "/compose": {
23
+ $post: {
24
+ input: {};
25
+ output: {
26
+ error: string;
27
+ };
28
+ outputFormat: "json";
29
+ status: 501;
30
+ };
31
+ };
32
+ } & {
33
+ "/:id/duplicate": {
34
+ $post: {
35
+ input: {
36
+ param: {
37
+ id: string;
38
+ };
39
+ };
40
+ output: {
41
+ error: string;
42
+ };
43
+ outputFormat: "json";
44
+ status: 501;
45
+ };
46
+ };
47
+ }, "/", "/:id/duplicate">;
48
+ export declare const catalogAuthoringExtension: HonoExtension;
49
+ export {};
50
+ //# sourceMappingURL=extension.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAGjE;;;;;;;;;;;GAWG;AAEH,KAAK,GAAG,GAAG;IACT,SAAS,EAAE;QACT,EAAE,EAAE,kBAAkB,CAAA;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAA;CACF,CAAA;AAED,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;yBAEwC,CAAA;AAO3E,eAAO,MAAM,yBAAyB,EAAE,aAGvC,CAAA"}
@@ -0,0 +1,12 @@
1
+ import { Hono } from "hono";
2
+ export const catalogAuthoringRoutes = new Hono()
3
+ .post("/compose", (c) => c.json({ error: "not_implemented" }, 501))
4
+ .post("/:id/duplicate", (c) => c.json({ error: "not_implemented" }, 501));
5
+ const catalogAuthoringExtensionDef = {
6
+ name: "catalog-authoring",
7
+ module: "products",
8
+ };
9
+ export const catalogAuthoringExtension = {
10
+ extension: catalogAuthoringExtensionDef,
11
+ adminRoutes: catalogAuthoringRoutes,
12
+ };
@@ -0,0 +1,7 @@
1
+ import type { Module } from "@voyantjs/core";
2
+ export declare const catalogAuthoringModule: Module;
3
+ export { catalogAuthoringExtension, catalogAuthoringRoutes } from "./extension.js";
4
+ export type { NewProductAuthoringRequest, ProductAuthoringRequest } from "./schema.js";
5
+ export { productAuthoringRequests } from "./schema.js";
6
+ export { type ProductGraphSpec, productGraphSpecSchema, } from "./spec.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAE5C,eAAO,MAAM,sBAAsB,EAAE,MAEpC,CAAA;AAED,OAAO,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AAClF,YAAY,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACtF,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,EACL,KAAK,gBAAgB,EACrB,sBAAsB,GACvB,MAAM,WAAW,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export const catalogAuthoringModule = {
2
+ name: "catalog-authoring",
3
+ };
4
+ export { catalogAuthoringExtension, catalogAuthoringRoutes } from "./extension.js";
5
+ export { productAuthoringRequests } from "./schema.js";
6
+ export { productGraphSpecSchema, } from "./spec.js";
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Dedup ledger for clone/compose. A clone or compose has no natural key
3
+ * (product names may collide), so we cannot reuse the booking-create
4
+ * advisory-lock-on-natural-key guard. Instead, callers (agent tool runtimes)
5
+ * pass an `Idempotency-Key`; a retried/double-tapped request with the same key
6
+ * returns the originally-created product id instead of building a second graph.
7
+ */
8
+ export declare const productAuthoringRequests: import("drizzle-orm/pg-core").PgTableWithColumns<{
9
+ name: "product_authoring_requests";
10
+ schema: undefined;
11
+ columns: {
12
+ idempotencyKey: import("drizzle-orm/pg-core").PgColumn<{
13
+ name: "idempotency_key";
14
+ tableName: "product_authoring_requests";
15
+ dataType: "string";
16
+ columnType: "PgText";
17
+ data: string;
18
+ driverParam: string;
19
+ notNull: true;
20
+ hasDefault: false;
21
+ isPrimaryKey: true;
22
+ isAutoincrement: false;
23
+ hasRuntimeDefault: false;
24
+ enumValues: [string, ...string[]];
25
+ baseColumn: never;
26
+ identity: undefined;
27
+ generated: undefined;
28
+ }, {}, {}>;
29
+ productId: import("drizzle-orm/pg-core").PgColumn<{
30
+ name: "product_id";
31
+ tableName: "product_authoring_requests";
32
+ dataType: "string";
33
+ columnType: "PgText";
34
+ data: string;
35
+ driverParam: string;
36
+ notNull: true;
37
+ hasDefault: false;
38
+ isPrimaryKey: false;
39
+ isAutoincrement: false;
40
+ hasRuntimeDefault: false;
41
+ enumValues: [string, ...string[]];
42
+ baseColumn: never;
43
+ identity: undefined;
44
+ generated: undefined;
45
+ }, {}, {}>;
46
+ operation: import("drizzle-orm/pg-core").PgColumn<{
47
+ name: "operation";
48
+ tableName: "product_authoring_requests";
49
+ dataType: "string";
50
+ columnType: "PgText";
51
+ data: string;
52
+ driverParam: string;
53
+ notNull: true;
54
+ hasDefault: false;
55
+ isPrimaryKey: false;
56
+ isAutoincrement: false;
57
+ hasRuntimeDefault: false;
58
+ enumValues: [string, ...string[]];
59
+ baseColumn: never;
60
+ identity: undefined;
61
+ generated: undefined;
62
+ }, {}, {}>;
63
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
64
+ name: "created_at";
65
+ tableName: "product_authoring_requests";
66
+ dataType: "date";
67
+ columnType: "PgTimestamp";
68
+ data: Date;
69
+ driverParam: string;
70
+ notNull: true;
71
+ hasDefault: true;
72
+ isPrimaryKey: false;
73
+ isAutoincrement: false;
74
+ hasRuntimeDefault: false;
75
+ enumValues: undefined;
76
+ baseColumn: never;
77
+ identity: undefined;
78
+ generated: undefined;
79
+ }, {}, {}>;
80
+ };
81
+ dialect: "pg";
82
+ }>;
83
+ export type ProductAuthoringRequest = typeof productAuthoringRequests.$inferSelect;
84
+ export type NewProductAuthoringRequest = typeof productAuthoringRequests.$inferInsert;
85
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWpC,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG,OAAO,wBAAwB,CAAC,YAAY,CAAA;AAClF,MAAM,MAAM,0BAA0B,GAAG,OAAO,wBAAwB,CAAC,YAAY,CAAA"}
package/dist/schema.js ADDED
@@ -0,0 +1,16 @@
1
+ import { index, pgTable, text, timestamp } from "drizzle-orm/pg-core";
2
+ /**
3
+ * Dedup ledger for clone/compose. A clone or compose has no natural key
4
+ * (product names may collide), so we cannot reuse the booking-create
5
+ * advisory-lock-on-natural-key guard. Instead, callers (agent tool runtimes)
6
+ * pass an `Idempotency-Key`; a retried/double-tapped request with the same key
7
+ * returns the originally-created product id instead of building a second graph.
8
+ */
9
+ export const productAuthoringRequests = pgTable("product_authoring_requests", {
10
+ idempotencyKey: text("idempotency_key").primaryKey(),
11
+ /** The product produced by the first request with this key. */
12
+ productId: text("product_id").notNull(),
13
+ /** `duplicate` | `compose` — for observability only. */
14
+ operation: text("operation").notNull(),
15
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
16
+ }, (t) => [index("idx_par_product").on(t.productId)]);
package/dist/spec.d.ts ADDED
@@ -0,0 +1,506 @@
1
+ import { z } from "zod";
2
+ export declare const unitSpecSchema: z.ZodObject<{
3
+ ref: z.ZodString;
4
+ name: z.ZodString;
5
+ code: z.ZodOptional<z.ZodNullable<z.ZodString>>;
6
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
7
+ unitType: z.ZodDefault<z.ZodEnum<{
8
+ person: "person";
9
+ group: "group";
10
+ room: "room";
11
+ vehicle: "vehicle";
12
+ service: "service";
13
+ other: "other";
14
+ }>>;
15
+ minQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
16
+ maxQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
17
+ minAge: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
18
+ maxAge: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
19
+ occupancyMin: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
20
+ occupancyMax: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
21
+ isRequired: z.ZodDefault<z.ZodBoolean>;
22
+ isHidden: z.ZodDefault<z.ZodBoolean>;
23
+ sortOrder: z.ZodDefault<z.ZodNumber>;
24
+ }, z.core.$strip>;
25
+ export declare const unitTierSpecSchema: z.ZodObject<{
26
+ minQuantity: z.ZodNumber;
27
+ maxQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
28
+ sellAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
29
+ costAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
30
+ active: z.ZodDefault<z.ZodBoolean>;
31
+ sortOrder: z.ZodDefault<z.ZodNumber>;
32
+ }, z.core.$strip>;
33
+ export declare const unitPriceRuleSpecSchema: z.ZodObject<{
34
+ unitRef: z.ZodString;
35
+ pricingCategoryId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
36
+ pricingMode: z.ZodDefault<z.ZodEnum<{
37
+ per_unit: "per_unit";
38
+ per_person: "per_person";
39
+ per_booking: "per_booking";
40
+ included: "included";
41
+ free: "free";
42
+ on_request: "on_request";
43
+ }>>;
44
+ sellAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
45
+ costAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
46
+ minQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
47
+ maxQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
48
+ active: z.ZodDefault<z.ZodBoolean>;
49
+ sortOrder: z.ZodDefault<z.ZodNumber>;
50
+ notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
51
+ tiers: z.ZodDefault<z.ZodArray<z.ZodObject<{
52
+ minQuantity: z.ZodNumber;
53
+ maxQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
54
+ sellAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
55
+ costAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
56
+ active: z.ZodDefault<z.ZodBoolean>;
57
+ sortOrder: z.ZodDefault<z.ZodNumber>;
58
+ }, z.core.$strip>>>;
59
+ }, z.core.$strip>;
60
+ export declare const optionPriceRuleSpecSchema: z.ZodObject<{
61
+ priceCatalogId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
62
+ priceScheduleId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
63
+ cancellationPolicyId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
64
+ code: z.ZodOptional<z.ZodNullable<z.ZodString>>;
65
+ name: z.ZodString;
66
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
67
+ pricingMode: z.ZodDefault<z.ZodEnum<{
68
+ per_person: "per_person";
69
+ per_booking: "per_booking";
70
+ free: "free";
71
+ on_request: "on_request";
72
+ starting_from: "starting_from";
73
+ }>>;
74
+ baseSellAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
75
+ baseCostAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
76
+ minPerBooking: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
77
+ maxPerBooking: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
78
+ allPricingCategories: z.ZodDefault<z.ZodBoolean>;
79
+ isDefault: z.ZodDefault<z.ZodBoolean>;
80
+ active: z.ZodDefault<z.ZodBoolean>;
81
+ unitPriceRules: z.ZodDefault<z.ZodArray<z.ZodObject<{
82
+ unitRef: z.ZodString;
83
+ pricingCategoryId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
84
+ pricingMode: z.ZodDefault<z.ZodEnum<{
85
+ per_unit: "per_unit";
86
+ per_person: "per_person";
87
+ per_booking: "per_booking";
88
+ included: "included";
89
+ free: "free";
90
+ on_request: "on_request";
91
+ }>>;
92
+ sellAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
93
+ costAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
94
+ minQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
95
+ maxQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
96
+ active: z.ZodDefault<z.ZodBoolean>;
97
+ sortOrder: z.ZodDefault<z.ZodNumber>;
98
+ notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
99
+ tiers: z.ZodDefault<z.ZodArray<z.ZodObject<{
100
+ minQuantity: z.ZodNumber;
101
+ maxQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
102
+ sellAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
103
+ costAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
104
+ active: z.ZodDefault<z.ZodBoolean>;
105
+ sortOrder: z.ZodDefault<z.ZodNumber>;
106
+ }, z.core.$strip>>>;
107
+ }, z.core.$strip>>>;
108
+ }, z.core.$strip>;
109
+ export declare const optionSpecSchema: z.ZodObject<{
110
+ ref: z.ZodString;
111
+ name: z.ZodString;
112
+ code: z.ZodOptional<z.ZodNullable<z.ZodString>>;
113
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
114
+ status: z.ZodDefault<z.ZodEnum<{
115
+ active: "active";
116
+ draft: "draft";
117
+ inactive: "inactive";
118
+ archived: "archived";
119
+ }>>;
120
+ isDefault: z.ZodDefault<z.ZodBoolean>;
121
+ sortOrder: z.ZodDefault<z.ZodNumber>;
122
+ availableFrom: z.ZodOptional<z.ZodNullable<z.ZodString>>;
123
+ availableTo: z.ZodOptional<z.ZodNullable<z.ZodString>>;
124
+ units: z.ZodDefault<z.ZodArray<z.ZodObject<{
125
+ ref: z.ZodString;
126
+ name: z.ZodString;
127
+ code: z.ZodOptional<z.ZodNullable<z.ZodString>>;
128
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
129
+ unitType: z.ZodDefault<z.ZodEnum<{
130
+ person: "person";
131
+ group: "group";
132
+ room: "room";
133
+ vehicle: "vehicle";
134
+ service: "service";
135
+ other: "other";
136
+ }>>;
137
+ minQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
138
+ maxQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
139
+ minAge: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
140
+ maxAge: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
141
+ occupancyMin: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
142
+ occupancyMax: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
143
+ isRequired: z.ZodDefault<z.ZodBoolean>;
144
+ isHidden: z.ZodDefault<z.ZodBoolean>;
145
+ sortOrder: z.ZodDefault<z.ZodNumber>;
146
+ }, z.core.$strip>>>;
147
+ priceRules: z.ZodDefault<z.ZodArray<z.ZodObject<{
148
+ priceCatalogId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
149
+ priceScheduleId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
150
+ cancellationPolicyId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
151
+ code: z.ZodOptional<z.ZodNullable<z.ZodString>>;
152
+ name: z.ZodString;
153
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
154
+ pricingMode: z.ZodDefault<z.ZodEnum<{
155
+ per_person: "per_person";
156
+ per_booking: "per_booking";
157
+ free: "free";
158
+ on_request: "on_request";
159
+ starting_from: "starting_from";
160
+ }>>;
161
+ baseSellAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
162
+ baseCostAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
163
+ minPerBooking: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
164
+ maxPerBooking: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
165
+ allPricingCategories: z.ZodDefault<z.ZodBoolean>;
166
+ isDefault: z.ZodDefault<z.ZodBoolean>;
167
+ active: z.ZodDefault<z.ZodBoolean>;
168
+ unitPriceRules: z.ZodDefault<z.ZodArray<z.ZodObject<{
169
+ unitRef: z.ZodString;
170
+ pricingCategoryId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
171
+ pricingMode: z.ZodDefault<z.ZodEnum<{
172
+ per_unit: "per_unit";
173
+ per_person: "per_person";
174
+ per_booking: "per_booking";
175
+ included: "included";
176
+ free: "free";
177
+ on_request: "on_request";
178
+ }>>;
179
+ sellAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
180
+ costAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
181
+ minQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
182
+ maxQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
183
+ active: z.ZodDefault<z.ZodBoolean>;
184
+ sortOrder: z.ZodDefault<z.ZodNumber>;
185
+ notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
186
+ tiers: z.ZodDefault<z.ZodArray<z.ZodObject<{
187
+ minQuantity: z.ZodNumber;
188
+ maxQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
189
+ sellAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
190
+ costAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
191
+ active: z.ZodDefault<z.ZodBoolean>;
192
+ sortOrder: z.ZodDefault<z.ZodNumber>;
193
+ }, z.core.$strip>>>;
194
+ }, z.core.$strip>>>;
195
+ }, z.core.$strip>>>;
196
+ }, z.core.$strip>;
197
+ export declare const paxPricingTierSpecSchema: z.ZodObject<{
198
+ unitRef: z.ZodOptional<z.ZodNullable<z.ZodString>>;
199
+ tierPax: z.ZodNumber;
200
+ pricePerPaxCents: z.ZodNumber;
201
+ promoPricePerPaxCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
202
+ effectiveFrom: z.ZodOptional<z.ZodNullable<z.ZodString>>;
203
+ effectiveTo: z.ZodOptional<z.ZodNullable<z.ZodString>>;
204
+ }, z.core.$strip>;
205
+ export declare const dayServiceSpecSchema: z.ZodObject<{
206
+ supplierServiceId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
207
+ serviceType: z.ZodEnum<{
208
+ other: "other";
209
+ accommodation: "accommodation";
210
+ transfer: "transfer";
211
+ experience: "experience";
212
+ guide: "guide";
213
+ meal: "meal";
214
+ }>;
215
+ name: z.ZodString;
216
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
217
+ countryCode: z.ZodOptional<z.ZodNullable<z.ZodString>>;
218
+ costCurrency: z.ZodString;
219
+ costAmountCents: z.ZodNumber;
220
+ quantity: z.ZodDefault<z.ZodNumber>;
221
+ sortOrder: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
222
+ notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
223
+ }, z.core.$strip>;
224
+ export declare const daySpecSchema: z.ZodObject<{
225
+ dayNumber: z.ZodNumber;
226
+ title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
227
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
228
+ location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
229
+ services: z.ZodDefault<z.ZodArray<z.ZodObject<{
230
+ supplierServiceId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
231
+ serviceType: z.ZodEnum<{
232
+ other: "other";
233
+ accommodation: "accommodation";
234
+ transfer: "transfer";
235
+ experience: "experience";
236
+ guide: "guide";
237
+ meal: "meal";
238
+ }>;
239
+ name: z.ZodString;
240
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
241
+ countryCode: z.ZodOptional<z.ZodNullable<z.ZodString>>;
242
+ costCurrency: z.ZodString;
243
+ costAmountCents: z.ZodNumber;
244
+ quantity: z.ZodDefault<z.ZodNumber>;
245
+ sortOrder: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
246
+ notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
247
+ }, z.core.$strip>>>;
248
+ }, z.core.$strip>;
249
+ export declare const itinerarySpecSchema: z.ZodObject<{
250
+ name: z.ZodString;
251
+ isDefault: z.ZodDefault<z.ZodBoolean>;
252
+ sortOrder: z.ZodDefault<z.ZodNumber>;
253
+ days: z.ZodDefault<z.ZodArray<z.ZodObject<{
254
+ dayNumber: z.ZodNumber;
255
+ title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
256
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
257
+ location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
258
+ services: z.ZodDefault<z.ZodArray<z.ZodObject<{
259
+ supplierServiceId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
260
+ serviceType: z.ZodEnum<{
261
+ other: "other";
262
+ accommodation: "accommodation";
263
+ transfer: "transfer";
264
+ experience: "experience";
265
+ guide: "guide";
266
+ meal: "meal";
267
+ }>;
268
+ name: z.ZodString;
269
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
270
+ countryCode: z.ZodOptional<z.ZodNullable<z.ZodString>>;
271
+ costCurrency: z.ZodString;
272
+ costAmountCents: z.ZodNumber;
273
+ quantity: z.ZodDefault<z.ZodNumber>;
274
+ sortOrder: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
275
+ notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
276
+ }, z.core.$strip>>>;
277
+ }, z.core.$strip>>>;
278
+ }, z.core.$strip>;
279
+ export declare const productRowSpecSchema: z.ZodObject<{
280
+ name: z.ZodString;
281
+ status: z.ZodDefault<z.ZodEnum<{
282
+ active: "active";
283
+ draft: "draft";
284
+ inactive: "inactive";
285
+ archived: "archived";
286
+ }>>;
287
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
288
+ inclusionsHtml: z.ZodOptional<z.ZodNullable<z.ZodString>>;
289
+ exclusionsHtml: z.ZodOptional<z.ZodNullable<z.ZodString>>;
290
+ termsHtml: z.ZodOptional<z.ZodNullable<z.ZodString>>;
291
+ bookingMode: z.ZodDefault<z.ZodEnum<{
292
+ date: "date";
293
+ other: "other";
294
+ transfer: "transfer";
295
+ date_time: "date_time";
296
+ open: "open";
297
+ stay: "stay";
298
+ itinerary: "itinerary";
299
+ }>>;
300
+ capacityMode: z.ZodDefault<z.ZodEnum<{
301
+ limited: "limited";
302
+ unlimited: "unlimited";
303
+ }>>;
304
+ timezone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
305
+ defaultLanguageTag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
306
+ visibility: z.ZodDefault<z.ZodEnum<{
307
+ private: "private";
308
+ public: "public";
309
+ unlisted: "unlisted";
310
+ }>>;
311
+ sellCurrency: z.ZodString;
312
+ sellAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
313
+ costAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
314
+ marginPercent: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
315
+ facilityId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
316
+ supplierId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
317
+ startDate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
318
+ endDate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
319
+ pax: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
320
+ productTypeId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
321
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
322
+ }, z.core.$strip>;
323
+ export declare const productGraphSpecSchema: z.ZodObject<{
324
+ product: z.ZodObject<{
325
+ name: z.ZodString;
326
+ status: z.ZodDefault<z.ZodEnum<{
327
+ active: "active";
328
+ draft: "draft";
329
+ inactive: "inactive";
330
+ archived: "archived";
331
+ }>>;
332
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
333
+ inclusionsHtml: z.ZodOptional<z.ZodNullable<z.ZodString>>;
334
+ exclusionsHtml: z.ZodOptional<z.ZodNullable<z.ZodString>>;
335
+ termsHtml: z.ZodOptional<z.ZodNullable<z.ZodString>>;
336
+ bookingMode: z.ZodDefault<z.ZodEnum<{
337
+ date: "date";
338
+ other: "other";
339
+ transfer: "transfer";
340
+ date_time: "date_time";
341
+ open: "open";
342
+ stay: "stay";
343
+ itinerary: "itinerary";
344
+ }>>;
345
+ capacityMode: z.ZodDefault<z.ZodEnum<{
346
+ limited: "limited";
347
+ unlimited: "unlimited";
348
+ }>>;
349
+ timezone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
350
+ defaultLanguageTag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
351
+ visibility: z.ZodDefault<z.ZodEnum<{
352
+ private: "private";
353
+ public: "public";
354
+ unlisted: "unlisted";
355
+ }>>;
356
+ sellCurrency: z.ZodString;
357
+ sellAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
358
+ costAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
359
+ marginPercent: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
360
+ facilityId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
361
+ supplierId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
362
+ startDate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
363
+ endDate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
364
+ pax: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
365
+ productTypeId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
366
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
367
+ }, z.core.$strip>;
368
+ options: z.ZodDefault<z.ZodArray<z.ZodObject<{
369
+ ref: z.ZodString;
370
+ name: z.ZodString;
371
+ code: z.ZodOptional<z.ZodNullable<z.ZodString>>;
372
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
373
+ status: z.ZodDefault<z.ZodEnum<{
374
+ active: "active";
375
+ draft: "draft";
376
+ inactive: "inactive";
377
+ archived: "archived";
378
+ }>>;
379
+ isDefault: z.ZodDefault<z.ZodBoolean>;
380
+ sortOrder: z.ZodDefault<z.ZodNumber>;
381
+ availableFrom: z.ZodOptional<z.ZodNullable<z.ZodString>>;
382
+ availableTo: z.ZodOptional<z.ZodNullable<z.ZodString>>;
383
+ units: z.ZodDefault<z.ZodArray<z.ZodObject<{
384
+ ref: z.ZodString;
385
+ name: z.ZodString;
386
+ code: z.ZodOptional<z.ZodNullable<z.ZodString>>;
387
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
388
+ unitType: z.ZodDefault<z.ZodEnum<{
389
+ person: "person";
390
+ group: "group";
391
+ room: "room";
392
+ vehicle: "vehicle";
393
+ service: "service";
394
+ other: "other";
395
+ }>>;
396
+ minQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
397
+ maxQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
398
+ minAge: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
399
+ maxAge: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
400
+ occupancyMin: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
401
+ occupancyMax: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
402
+ isRequired: z.ZodDefault<z.ZodBoolean>;
403
+ isHidden: z.ZodDefault<z.ZodBoolean>;
404
+ sortOrder: z.ZodDefault<z.ZodNumber>;
405
+ }, z.core.$strip>>>;
406
+ priceRules: z.ZodDefault<z.ZodArray<z.ZodObject<{
407
+ priceCatalogId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
408
+ priceScheduleId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
409
+ cancellationPolicyId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
410
+ code: z.ZodOptional<z.ZodNullable<z.ZodString>>;
411
+ name: z.ZodString;
412
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
413
+ pricingMode: z.ZodDefault<z.ZodEnum<{
414
+ per_person: "per_person";
415
+ per_booking: "per_booking";
416
+ free: "free";
417
+ on_request: "on_request";
418
+ starting_from: "starting_from";
419
+ }>>;
420
+ baseSellAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
421
+ baseCostAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
422
+ minPerBooking: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
423
+ maxPerBooking: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
424
+ allPricingCategories: z.ZodDefault<z.ZodBoolean>;
425
+ isDefault: z.ZodDefault<z.ZodBoolean>;
426
+ active: z.ZodDefault<z.ZodBoolean>;
427
+ unitPriceRules: z.ZodDefault<z.ZodArray<z.ZodObject<{
428
+ unitRef: z.ZodString;
429
+ pricingCategoryId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
430
+ pricingMode: z.ZodDefault<z.ZodEnum<{
431
+ per_unit: "per_unit";
432
+ per_person: "per_person";
433
+ per_booking: "per_booking";
434
+ included: "included";
435
+ free: "free";
436
+ on_request: "on_request";
437
+ }>>;
438
+ sellAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
439
+ costAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
440
+ minQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
441
+ maxQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
442
+ active: z.ZodDefault<z.ZodBoolean>;
443
+ sortOrder: z.ZodDefault<z.ZodNumber>;
444
+ notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
445
+ tiers: z.ZodDefault<z.ZodArray<z.ZodObject<{
446
+ minQuantity: z.ZodNumber;
447
+ maxQuantity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
448
+ sellAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
449
+ costAmountCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
450
+ active: z.ZodDefault<z.ZodBoolean>;
451
+ sortOrder: z.ZodDefault<z.ZodNumber>;
452
+ }, z.core.$strip>>>;
453
+ }, z.core.$strip>>>;
454
+ }, z.core.$strip>>>;
455
+ }, z.core.$strip>>>;
456
+ paxPricingTiers: z.ZodDefault<z.ZodArray<z.ZodObject<{
457
+ unitRef: z.ZodOptional<z.ZodNullable<z.ZodString>>;
458
+ tierPax: z.ZodNumber;
459
+ pricePerPaxCents: z.ZodNumber;
460
+ promoPricePerPaxCents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
461
+ effectiveFrom: z.ZodOptional<z.ZodNullable<z.ZodString>>;
462
+ effectiveTo: z.ZodOptional<z.ZodNullable<z.ZodString>>;
463
+ }, z.core.$strip>>>;
464
+ itineraries: z.ZodDefault<z.ZodArray<z.ZodObject<{
465
+ name: z.ZodString;
466
+ isDefault: z.ZodDefault<z.ZodBoolean>;
467
+ sortOrder: z.ZodDefault<z.ZodNumber>;
468
+ days: z.ZodDefault<z.ZodArray<z.ZodObject<{
469
+ dayNumber: z.ZodNumber;
470
+ title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
471
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
472
+ location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
473
+ services: z.ZodDefault<z.ZodArray<z.ZodObject<{
474
+ supplierServiceId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
475
+ serviceType: z.ZodEnum<{
476
+ other: "other";
477
+ accommodation: "accommodation";
478
+ transfer: "transfer";
479
+ experience: "experience";
480
+ guide: "guide";
481
+ meal: "meal";
482
+ }>;
483
+ name: z.ZodString;
484
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
485
+ countryCode: z.ZodOptional<z.ZodNullable<z.ZodString>>;
486
+ costCurrency: z.ZodString;
487
+ costAmountCents: z.ZodNumber;
488
+ quantity: z.ZodDefault<z.ZodNumber>;
489
+ sortOrder: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
490
+ notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
491
+ }, z.core.$strip>>>;
492
+ }, z.core.$strip>>>;
493
+ }, z.core.$strip>>>;
494
+ }, z.core.$strip>;
495
+ export type UnitSpec = z.infer<typeof unitSpecSchema>;
496
+ export type UnitTierSpec = z.infer<typeof unitTierSpecSchema>;
497
+ export type UnitPriceRuleSpec = z.infer<typeof unitPriceRuleSpecSchema>;
498
+ export type OptionPriceRuleSpec = z.infer<typeof optionPriceRuleSpecSchema>;
499
+ export type OptionSpec = z.infer<typeof optionSpecSchema>;
500
+ export type PaxPricingTierSpec = z.infer<typeof paxPricingTierSpecSchema>;
501
+ export type DayServiceSpec = z.infer<typeof dayServiceSpecSchema>;
502
+ export type DaySpec = z.infer<typeof daySpecSchema>;
503
+ export type ItinerarySpec = z.infer<typeof itinerarySpecSchema>;
504
+ export type ProductRowSpec = z.infer<typeof productRowSpecSchema>;
505
+ export type ProductGraphSpec = z.infer<typeof productGraphSpecSchema>;
506
+ //# sourceMappingURL=spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAuBvB,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;iBAezB,CAAA;AAEF,eAAO,MAAM,kBAAkB;;;;;;;iBAO7B,CAAA;AAEF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;iBAelC,CAAA;AAEF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsBpC,CAAA;AAEF,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAY3B,CAAA;AAEF,eAAO,MAAM,wBAAwB;;;;;;;iBAQnC,CAAA;AAEF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;iBAW/B,CAAA;AAEF,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;iBAMxB,CAAA;AAEF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAK9B,CAAA;AAEF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyB/B,CAAA;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAKjC,CAAA;AAEF,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAA;AACrD,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAC7D,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAA;AACvE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAC3E,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AACzD,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAA;AACzE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACjE,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAA;AACnD,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACjE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA"}
package/dist/spec.js ADDED
@@ -0,0 +1,162 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Normalized, transport-agnostic description of a bookable product graph.
4
+ *
5
+ * Both entry points feed the same builder:
6
+ * - clone — serialize an existing product into a spec, patch the product row.
7
+ * - compose — caller (Max AI agent) supplies the spec directly.
8
+ *
9
+ * Cross-references inside an option (a unit price rule pointing at a sibling
10
+ * unit, a pax tier pointing at a unit) use **local ref keys** (`ref` / `unitRef`),
11
+ * which the builder resolves to freshly-minted ids. For clone, serialize sets
12
+ * each `ref` to the source row id; for compose, the caller picks any unique
13
+ * string. Refs never reach the database.
14
+ *
15
+ * NOTE: `availability_slots` (departures) are intentionally absent — they are
16
+ * date-specific and added separately after the graph exists.
17
+ *
18
+ * Home of this type is under review (here vs `@voyantjs/products-contracts`).
19
+ */
20
+ const money = z.number().int();
21
+ export const unitSpecSchema = z.object({
22
+ ref: z.string().min(1),
23
+ name: z.string().min(1).max(255),
24
+ code: z.string().max(255).nullish(),
25
+ description: z.string().nullish(),
26
+ unitType: z.enum(["person", "group", "room", "vehicle", "service", "other"]).default("person"),
27
+ minQuantity: z.number().int().nullish(),
28
+ maxQuantity: z.number().int().nullish(),
29
+ minAge: z.number().int().nullish(),
30
+ maxAge: z.number().int().nullish(),
31
+ occupancyMin: z.number().int().nullish(),
32
+ occupancyMax: z.number().int().nullish(),
33
+ isRequired: z.boolean().default(false),
34
+ isHidden: z.boolean().default(false),
35
+ sortOrder: z.number().int().default(0),
36
+ });
37
+ export const unitTierSpecSchema = z.object({
38
+ minQuantity: z.number().int(),
39
+ maxQuantity: z.number().int().nullish(),
40
+ sellAmountCents: money.nullish(),
41
+ costAmountCents: money.nullish(),
42
+ active: z.boolean().default(true),
43
+ sortOrder: z.number().int().default(0),
44
+ });
45
+ export const unitPriceRuleSpecSchema = z.object({
46
+ /** Resolves to a sibling unit's `ref` within the same option. */
47
+ unitRef: z.string().min(1),
48
+ pricingCategoryId: z.string().nullish(),
49
+ pricingMode: z
50
+ .enum(["per_unit", "per_person", "per_booking", "included", "free", "on_request"])
51
+ .default("per_unit"),
52
+ sellAmountCents: money.nullish(),
53
+ costAmountCents: money.nullish(),
54
+ minQuantity: z.number().int().nullish(),
55
+ maxQuantity: z.number().int().nullish(),
56
+ active: z.boolean().default(true),
57
+ sortOrder: z.number().int().default(0),
58
+ notes: z.string().nullish(),
59
+ tiers: z.array(unitTierSpecSchema).default([]),
60
+ });
61
+ export const optionPriceRuleSpecSchema = z.object({
62
+ /**
63
+ * Price catalog the rule points at. Optional: clone reuses the source rule's
64
+ * catalog; compose resolves the operator default when omitted.
65
+ */
66
+ priceCatalogId: z.string().nullish(),
67
+ priceScheduleId: z.string().nullish(),
68
+ cancellationPolicyId: z.string().nullish(),
69
+ code: z.string().nullish(),
70
+ name: z.string().min(1).max(255),
71
+ description: z.string().nullish(),
72
+ pricingMode: z
73
+ .enum(["per_person", "per_booking", "starting_from", "free", "on_request"])
74
+ .default("per_person"),
75
+ baseSellAmountCents: money.nullish(),
76
+ baseCostAmountCents: money.nullish(),
77
+ minPerBooking: z.number().int().nullish(),
78
+ maxPerBooking: z.number().int().nullish(),
79
+ allPricingCategories: z.boolean().default(true),
80
+ isDefault: z.boolean().default(false),
81
+ active: z.boolean().default(true),
82
+ unitPriceRules: z.array(unitPriceRuleSpecSchema).default([]),
83
+ });
84
+ export const optionSpecSchema = z.object({
85
+ ref: z.string().min(1),
86
+ name: z.string().min(1).max(255),
87
+ code: z.string().max(255).nullish(),
88
+ description: z.string().nullish(),
89
+ status: z.enum(["draft", "active", "inactive", "archived"]).default("draft"),
90
+ isDefault: z.boolean().default(false),
91
+ sortOrder: z.number().int().default(0),
92
+ availableFrom: z.string().nullish(),
93
+ availableTo: z.string().nullish(),
94
+ units: z.array(unitSpecSchema).default([]),
95
+ priceRules: z.array(optionPriceRuleSpecSchema).default([]),
96
+ });
97
+ export const paxPricingTierSpecSchema = z.object({
98
+ /** Optional link to a unit (by `ref`) within the spec. */
99
+ unitRef: z.string().nullish(),
100
+ tierPax: z.number().int(),
101
+ pricePerPaxCents: money,
102
+ promoPricePerPaxCents: money.nullish(),
103
+ effectiveFrom: z.string().nullish(),
104
+ effectiveTo: z.string().nullish(),
105
+ });
106
+ export const dayServiceSpecSchema = z.object({
107
+ supplierServiceId: z.string().nullish(),
108
+ serviceType: z.enum(["accommodation", "transfer", "experience", "guide", "meal", "other"]),
109
+ name: z.string().min(1).max(255),
110
+ description: z.string().nullish(),
111
+ countryCode: z.string().nullish(),
112
+ costCurrency: z.string().min(3).max(3),
113
+ costAmountCents: money,
114
+ quantity: z.number().int().default(1),
115
+ sortOrder: z.number().int().nullish(),
116
+ notes: z.string().nullish(),
117
+ });
118
+ export const daySpecSchema = z.object({
119
+ dayNumber: z.number().int(),
120
+ title: z.string().nullish(),
121
+ description: z.string().nullish(),
122
+ location: z.string().nullish(),
123
+ services: z.array(dayServiceSpecSchema).default([]),
124
+ });
125
+ export const itinerarySpecSchema = z.object({
126
+ name: z.string().min(1).max(255),
127
+ isDefault: z.boolean().default(false),
128
+ sortOrder: z.number().int().default(0),
129
+ days: z.array(daySpecSchema).default([]),
130
+ });
131
+ export const productRowSpecSchema = z.object({
132
+ name: z.string().min(1).max(255),
133
+ status: z.enum(["draft", "active", "inactive", "archived"]).default("draft"),
134
+ description: z.string().nullish(),
135
+ inclusionsHtml: z.string().nullish(),
136
+ exclusionsHtml: z.string().nullish(),
137
+ termsHtml: z.string().nullish(),
138
+ bookingMode: z
139
+ .enum(["date", "date_time", "open", "stay", "transfer", "itinerary", "other"])
140
+ .default("date"),
141
+ capacityMode: z.enum(["limited", "unlimited"]).default("limited"),
142
+ timezone: z.string().nullish(),
143
+ defaultLanguageTag: z.string().nullish(),
144
+ visibility: z.enum(["private", "public", "unlisted"]).default("private"),
145
+ sellCurrency: z.string().min(3).max(3),
146
+ sellAmountCents: money.nullish(),
147
+ costAmountCents: money.nullish(),
148
+ marginPercent: z.number().int().nullish(),
149
+ facilityId: z.string().nullish(),
150
+ supplierId: z.string().nullish(),
151
+ startDate: z.string().nullish(),
152
+ endDate: z.string().nullish(),
153
+ pax: z.number().int().nullish(),
154
+ productTypeId: z.string().nullish(),
155
+ tags: z.array(z.string()).default([]),
156
+ });
157
+ export const productGraphSpecSchema = z.object({
158
+ product: productRowSpecSchema,
159
+ options: z.array(optionSpecSchema).default([]),
160
+ paxPricingTiers: z.array(paxPricingTierSpecSchema).default([]),
161
+ itineraries: z.array(itinerarySpecSchema).default([]),
162
+ });
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@voyantjs/catalog-authoring",
3
+ "version": "0.104.1",
4
+ "license": "Apache-2.0",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/index.ts",
8
+ "./schema": "./src/schema.ts",
9
+ "./spec": "./src/spec.ts",
10
+ "./extension": "./src/extension.ts"
11
+ },
12
+ "scripts": {
13
+ "typecheck": "tsc --noEmit",
14
+ "lint": "biome check src/",
15
+ "test": "vitest run",
16
+ "build": "tsc -p tsconfig.json",
17
+ "clean": "rm -rf dist tsconfig.tsbuildinfo",
18
+ "prepack": "pnpm run build"
19
+ },
20
+ "dependencies": {
21
+ "@voyantjs/core": "workspace:^",
22
+ "@voyantjs/db": "workspace:^",
23
+ "@voyantjs/hono": "workspace:^",
24
+ "@voyantjs/pricing": "workspace:^",
25
+ "@voyantjs/products": "workspace:^",
26
+ "drizzle-orm": "^0.45.2",
27
+ "hono": "^4.12.10",
28
+ "zod": "^4.3.6"
29
+ },
30
+ "devDependencies": {
31
+ "@voyantjs/voyant-test-utils": "workspace:^",
32
+ "@voyantjs/voyant-typescript-config": "workspace:^",
33
+ "typescript": "^6.0.2"
34
+ },
35
+ "files": [
36
+ "dist"
37
+ ],
38
+ "publishConfig": {
39
+ "access": "public",
40
+ "exports": {
41
+ ".": {
42
+ "types": "./dist/index.d.ts",
43
+ "import": "./dist/index.js",
44
+ "default": "./dist/index.js"
45
+ },
46
+ "./schema": {
47
+ "types": "./dist/schema.d.ts",
48
+ "import": "./dist/schema.js",
49
+ "default": "./dist/schema.js"
50
+ },
51
+ "./spec": {
52
+ "types": "./dist/spec.d.ts",
53
+ "import": "./dist/spec.js",
54
+ "default": "./dist/spec.js"
55
+ },
56
+ "./extension": {
57
+ "types": "./dist/extension.d.ts",
58
+ "import": "./dist/extension.js",
59
+ "default": "./dist/extension.js"
60
+ }
61
+ },
62
+ "main": "./dist/index.js",
63
+ "types": "./dist/index.d.ts"
64
+ },
65
+ "repository": {
66
+ "type": "git",
67
+ "url": "https://github.com/voyantjs/voyant.git",
68
+ "directory": "packages/catalog-authoring"
69
+ },
70
+ "voyant": {
71
+ "schema": "./schema",
72
+ "requiresSchemas": [
73
+ "@voyantjs/db",
74
+ "@voyantjs/products",
75
+ "@voyantjs/pricing"
76
+ ]
77
+ }
78
+ }