@voyant-travel/inventory 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.
- package/LICENSE +201 -0
- package/dist/action-ledger-drift.d.ts +29 -0
- package/dist/action-ledger-drift.d.ts.map +1 -0
- package/dist/action-ledger-drift.js +338 -0
- package/dist/action-ledger.d.ts +104 -0
- package/dist/action-ledger.d.ts.map +1 -0
- package/dist/action-ledger.js +100 -0
- package/dist/authoring/builder.d.ts +37 -0
- package/dist/authoring/builder.d.ts.map +1 -0
- package/dist/authoring/builder.js +248 -0
- package/dist/authoring/clone-content.d.ts +38 -0
- package/dist/authoring/clone-content.d.ts.map +1 -0
- package/dist/authoring/clone-content.js +367 -0
- package/dist/authoring/clone-pricing.d.ts +9 -0
- package/dist/authoring/clone-pricing.d.ts.map +1 -0
- package/dist/authoring/clone-pricing.js +242 -0
- package/dist/authoring/clone.d.ts +45 -0
- package/dist/authoring/clone.d.ts.map +1 -0
- package/dist/authoring/clone.js +142 -0
- package/dist/authoring/errors.d.ts +21 -0
- package/dist/authoring/errors.d.ts.map +1 -0
- package/dist/authoring/errors.js +13 -0
- package/dist/authoring/extension.d.ts +248 -0
- package/dist/authoring/extension.d.ts.map +1 -0
- package/dist/authoring/extension.js +116 -0
- package/dist/authoring/index.d.ts +12 -0
- package/dist/authoring/index.d.ts.map +1 -0
- package/dist/authoring/index.js +11 -0
- package/dist/authoring/schema.d.ts +85 -0
- package/dist/authoring/schema.d.ts.map +1 -0
- package/dist/authoring/schema.js +16 -0
- package/dist/authoring/service.d.ts +28 -0
- package/dist/authoring/service.d.ts.map +1 -0
- package/dist/authoring/service.js +66 -0
- package/dist/authoring/spec.d.ts +524 -0
- package/dist/authoring/spec.d.ts.map +1 -0
- package/dist/authoring/spec.js +167 -0
- package/dist/authoring/validate.d.ts +17 -0
- package/dist/authoring/validate.d.ts.map +1 -0
- package/dist/authoring/validate.js +83 -0
- package/dist/authoring.d.ts +2 -0
- package/dist/authoring.d.ts.map +1 -0
- package/dist/authoring.js +1 -0
- package/dist/booking-engine/handler-support.d.ts +91 -0
- package/dist/booking-engine/handler-support.d.ts.map +1 -0
- package/dist/booking-engine/handler-support.js +355 -0
- package/dist/booking-engine/handler.d.ts +404 -0
- package/dist/booking-engine/handler.d.ts.map +1 -0
- package/dist/booking-engine/handler.js +398 -0
- package/dist/booking-engine/index.d.ts +8 -0
- package/dist/booking-engine/index.d.ts.map +1 -0
- package/dist/booking-engine/index.js +7 -0
- package/dist/booking-engine.d.ts +2 -0
- package/dist/booking-engine.d.ts.map +1 -0
- package/dist/booking-engine.js +1 -0
- package/dist/booking-extension.d.ts +278 -0
- package/dist/booking-extension.d.ts.map +1 -0
- package/dist/booking-extension.js +161 -0
- package/dist/catalog-policy-departures.d.ts +52 -0
- package/dist/catalog-policy-departures.d.ts.map +1 -0
- package/dist/catalog-policy-departures.js +169 -0
- package/dist/catalog-policy-destinations.d.ts +43 -0
- package/dist/catalog-policy-destinations.d.ts.map +1 -0
- package/dist/catalog-policy-destinations.js +165 -0
- package/dist/catalog-policy-pricing.d.ts +55 -0
- package/dist/catalog-policy-pricing.d.ts.map +1 -0
- package/dist/catalog-policy-pricing.js +109 -0
- package/dist/catalog-policy-promotions.d.ts +52 -0
- package/dist/catalog-policy-promotions.d.ts.map +1 -0
- package/dist/catalog-policy-promotions.js +270 -0
- package/dist/catalog-policy-taxonomy.d.ts +51 -0
- package/dist/catalog-policy-taxonomy.d.ts.map +1 -0
- package/dist/catalog-policy-taxonomy.js +191 -0
- package/dist/catalog-policy.d.ts +33 -0
- package/dist/catalog-policy.d.ts.map +1 -0
- package/dist/catalog-policy.js +733 -0
- package/dist/content-shape.d.ts +15 -0
- package/dist/content-shape.d.ts.map +1 -0
- package/dist/content-shape.js +28 -0
- package/dist/draft-shape.d.ts +43 -0
- package/dist/draft-shape.d.ts.map +1 -0
- package/dist/draft-shape.js +48 -0
- package/dist/events.d.ts +37 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +32 -0
- package/dist/extras/catalog-policy.d.ts +30 -0
- package/dist/extras/catalog-policy.d.ts.map +1 -0
- package/dist/extras/catalog-policy.js +319 -0
- package/dist/extras/content-shape.d.ts +5 -0
- package/dist/extras/content-shape.d.ts.map +1 -0
- package/dist/extras/content-shape.js +13 -0
- package/dist/extras/draft-shape.d.ts +34 -0
- package/dist/extras/draft-shape.d.ts.map +1 -0
- package/dist/extras/draft-shape.js +69 -0
- package/dist/extras/routes.d.ts +380 -0
- package/dist/extras/routes.d.ts.map +1 -0
- package/dist/extras/routes.js +59 -0
- package/dist/extras/schema-sourced-content.d.ts +254 -0
- package/dist/extras/schema-sourced-content.d.ts.map +1 -0
- package/dist/extras/schema-sourced-content.js +45 -0
- package/dist/extras/schema.d.ts +628 -0
- package/dist/extras/schema.d.ts.map +1 -0
- package/dist/extras/schema.js +87 -0
- package/dist/extras/service-catalog-plane.d.ts +77 -0
- package/dist/extras/service-catalog-plane.d.ts.map +1 -0
- package/dist/extras/service-catalog-plane.js +219 -0
- package/dist/extras/service-content-synthesizer.d.ts +41 -0
- package/dist/extras/service-content-synthesizer.d.ts.map +1 -0
- package/dist/extras/service-content-synthesizer.js +138 -0
- package/dist/extras/service-content.d.ts +48 -0
- package/dist/extras/service-content.d.ts.map +1 -0
- package/dist/extras/service-content.js +253 -0
- package/dist/extras/service.d.ts +185 -0
- package/dist/extras/service.d.ts.map +1 -0
- package/dist/extras/service.js +96 -0
- package/dist/extras/validation.d.ts +437 -0
- package/dist/extras/validation.d.ts.map +1 -0
- package/dist/extras/validation.js +149 -0
- package/dist/extras.d.ts +267 -0
- package/dist/extras.d.ts.map +1 -0
- package/dist/extras.js +19 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/interface.d.ts +5869 -0
- package/dist/interface.d.ts.map +1 -0
- package/dist/interface.js +54 -0
- package/dist/public-routes.d.ts +2 -0
- package/dist/public-routes.d.ts.map +1 -0
- package/dist/public-routes.js +1 -0
- package/dist/public-validation.d.ts +2 -0
- package/dist/public-validation.d.ts.map +1 -0
- package/dist/public-validation.js +1 -0
- package/dist/read-model.d.ts +25 -0
- package/dist/read-model.d.ts.map +1 -0
- package/dist/read-model.js +99 -0
- package/dist/route-env.d.ts +22 -0
- package/dist/route-env.d.ts.map +1 -0
- package/dist/route-env.js +1 -0
- package/dist/routes-associations.d.ts +164 -0
- package/dist/routes-associations.d.ts.map +1 -0
- package/dist/routes-associations.js +100 -0
- package/dist/routes-catalog.d.ts +436 -0
- package/dist/routes-catalog.d.ts.map +1 -0
- package/dist/routes-catalog.js +104 -0
- package/dist/routes-configuration.d.ts +773 -0
- package/dist/routes-configuration.d.ts.map +1 -0
- package/dist/routes-configuration.js +364 -0
- package/dist/routes-content.d.ts +74 -0
- package/dist/routes-content.d.ts.map +1 -0
- package/dist/routes-content.js +117 -0
- package/dist/routes-core.d.ts +331 -0
- package/dist/routes-core.d.ts.map +1 -0
- package/dist/routes-core.js +95 -0
- package/dist/routes-itinerary.d.ts +759 -0
- package/dist/routes-itinerary.d.ts.map +1 -0
- package/dist/routes-itinerary.js +387 -0
- package/dist/routes-maintenance.d.ts +32 -0
- package/dist/routes-maintenance.d.ts.map +1 -0
- package/dist/routes-maintenance.js +14 -0
- package/dist/routes-media.d.ts +634 -0
- package/dist/routes-media.d.ts.map +1 -0
- package/dist/routes-media.js +245 -0
- package/dist/routes-merchandising.d.ts +1120 -0
- package/dist/routes-merchandising.d.ts.map +1 -0
- package/dist/routes-merchandising.js +377 -0
- package/dist/routes-options.d.ts +363 -0
- package/dist/routes-options.d.ts.map +1 -0
- package/dist/routes-options.js +173 -0
- package/dist/routes-public.d.ts +776 -0
- package/dist/routes-public.d.ts.map +1 -0
- package/dist/routes-public.js +119 -0
- package/dist/routes-translations.d.ts +489 -0
- package/dist/routes-translations.d.ts.map +1 -0
- package/dist/routes-translations.js +258 -0
- package/dist/routes.d.ts +5097 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +64 -0
- package/dist/schema-core.d.ts +1238 -0
- package/dist/schema-core.d.ts.map +1 -0
- package/dist/schema-core.js +157 -0
- package/dist/schema-itinerary.d.ts +1169 -0
- package/dist/schema-itinerary.d.ts.map +1 -0
- package/dist/schema-itinerary.js +130 -0
- package/dist/schema-relations.d.ts +117 -0
- package/dist/schema-relations.d.ts.map +1 -0
- package/dist/schema-relations.js +192 -0
- package/dist/schema-settings.d.ts +1800 -0
- package/dist/schema-settings.d.ts.map +1 -0
- package/dist/schema-settings.js +220 -0
- package/dist/schema-shared.d.ts +15 -0
- package/dist/schema-shared.d.ts.map +1 -0
- package/dist/schema-shared.js +91 -0
- package/dist/schema-sourced-content.d.ts +262 -0
- package/dist/schema-sourced-content.d.ts.map +1 -0
- package/dist/schema-sourced-content.js +69 -0
- package/dist/schema-taxonomy.d.ts +1363 -0
- package/dist/schema-taxonomy.d.ts.map +1 -0
- package/dist/schema-taxonomy.js +203 -0
- package/dist/schema.d.ts +10 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +9 -0
- package/dist/service-aggregates.d.ts +29 -0
- package/dist/service-aggregates.d.ts.map +1 -0
- package/dist/service-aggregates.js +56 -0
- package/dist/service-catalog-plane-destinations.d.ts +30 -0
- package/dist/service-catalog-plane-destinations.d.ts.map +1 -0
- package/dist/service-catalog-plane-destinations.js +143 -0
- package/dist/service-catalog-plane-taxonomy.d.ts +73 -0
- package/dist/service-catalog-plane-taxonomy.d.ts.map +1 -0
- package/dist/service-catalog-plane-taxonomy.js +242 -0
- package/dist/service-catalog-plane.d.ts +179 -0
- package/dist/service-catalog-plane.d.ts.map +1 -0
- package/dist/service-catalog-plane.js +431 -0
- package/dist/service-catalog.d.ts +251 -0
- package/dist/service-catalog.d.ts.map +1 -0
- package/dist/service-catalog.js +517 -0
- package/dist/service-configuration.d.ts +261 -0
- package/dist/service-configuration.d.ts.map +1 -0
- package/dist/service-configuration.js +343 -0
- package/dist/service-content-owned.d.ts +68 -0
- package/dist/service-content-owned.d.ts.map +1 -0
- package/dist/service-content-owned.js +329 -0
- package/dist/service-content-synthesizer.d.ts +90 -0
- package/dist/service-content-synthesizer.d.ts.map +1 -0
- package/dist/service-content-synthesizer.js +178 -0
- package/dist/service-content.d.ts +106 -0
- package/dist/service-content.d.ts.map +1 -0
- package/dist/service-content.js +388 -0
- package/dist/service-core.d.ts +194 -0
- package/dist/service-core.d.ts.map +1 -0
- package/dist/service-core.js +213 -0
- package/dist/service-delivery-formats.d.ts +58 -0
- package/dist/service-delivery-formats.d.ts.map +1 -0
- package/dist/service-delivery-formats.js +107 -0
- package/dist/service-destinations.d.ts +223 -0
- package/dist/service-destinations.d.ts.map +1 -0
- package/dist/service-destinations.js +310 -0
- package/dist/service-itinerary-history.d.ts +457 -0
- package/dist/service-itinerary-history.d.ts.map +1 -0
- package/dist/service-itinerary-history.js +135 -0
- package/dist/service-itinerary.d.ts +1149 -0
- package/dist/service-itinerary.d.ts.map +1 -0
- package/dist/service-itinerary.js +419 -0
- package/dist/service-media.d.ts +272 -0
- package/dist/service-media.d.ts.map +1 -0
- package/dist/service-media.js +320 -0
- package/dist/service-merchandising.d.ts +184 -0
- package/dist/service-merchandising.d.ts.map +1 -0
- package/dist/service-merchandising.js +181 -0
- package/dist/service-option-translations.d.ts +268 -0
- package/dist/service-option-translations.d.ts.map +1 -0
- package/dist/service-option-translations.js +300 -0
- package/dist/service-options.d.ts +181 -0
- package/dist/service-options.d.ts.map +1 -0
- package/dist/service-options.js +179 -0
- package/dist/service-product-destinations.d.ts +37 -0
- package/dist/service-product-destinations.d.ts.map +1 -0
- package/dist/service-product-destinations.js +94 -0
- package/dist/service-public.d.ts +664 -0
- package/dist/service-public.d.ts.map +1 -0
- package/dist/service-public.js +374 -0
- package/dist/service-taxonomy.d.ts +197 -0
- package/dist/service-taxonomy.d.ts.map +1 -0
- package/dist/service-taxonomy.js +221 -0
- package/dist/service.d.ts +3929 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +28 -0
- package/dist/tasks/brochure-printers.d.ts +31 -0
- package/dist/tasks/brochure-printers.d.ts.map +1 -0
- package/dist/tasks/brochure-printers.js +149 -0
- package/dist/tasks/brochure-templates.d.ts +36 -0
- package/dist/tasks/brochure-templates.d.ts.map +1 -0
- package/dist/tasks/brochure-templates.js +110 -0
- package/dist/tasks/brochures.d.ts +43 -0
- package/dist/tasks/brochures.d.ts.map +1 -0
- package/dist/tasks/brochures.js +72 -0
- package/dist/tasks/generate-pdf.d.ts +8 -0
- package/dist/tasks/generate-pdf.d.ts.map +1 -0
- package/dist/tasks/generate-pdf.js +106 -0
- package/dist/tasks/index.d.ts +5 -0
- package/dist/tasks/index.d.ts.map +1 -0
- package/dist/tasks/index.js +4 -0
- package/dist/tasks/pdf-text.d.ts +2 -0
- package/dist/tasks/pdf-text.d.ts.map +1 -0
- package/dist/tasks/pdf-text.js +40 -0
- package/dist/tasks.d.ts +2 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +1 -0
- package/dist/validation-catalog.d.ts +2 -0
- package/dist/validation-catalog.d.ts.map +1 -0
- package/dist/validation-catalog.js +3 -0
- package/dist/validation-config.d.ts +2 -0
- package/dist/validation-config.d.ts.map +1 -0
- package/dist/validation-config.js +3 -0
- package/dist/validation-content.d.ts +2 -0
- package/dist/validation-content.d.ts.map +1 -0
- package/dist/validation-content.js +3 -0
- package/dist/validation-core.d.ts +2 -0
- package/dist/validation-core.d.ts.map +1 -0
- package/dist/validation-core.js +3 -0
- package/dist/validation-public.d.ts +2 -0
- package/dist/validation-public.d.ts.map +1 -0
- package/dist/validation-public.js +3 -0
- package/dist/validation-shared.d.ts +2 -0
- package/dist/validation-shared.d.ts.map +1 -0
- package/dist/validation-shared.js +3 -0
- package/dist/validation.d.ts +2 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +3 -0
- package/package.json +204 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { eq, inArray, sql } from "drizzle-orm";
|
|
2
|
+
import { optionUnits, productOptions, products } from "../schema.js";
|
|
3
|
+
import { productsService } from "../service.js";
|
|
4
|
+
import { copyProductContent, withoutSystemColumns } from "./clone-content.js";
|
|
5
|
+
import { copyPricingAndAvailability } from "./clone-pricing.js";
|
|
6
|
+
import { productAuthoringRequests } from "./schema.js";
|
|
7
|
+
function copyName(name) {
|
|
8
|
+
return `${name} (Copy)`;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Reads a product's options + units back into the {@link ClonedOption} shape.
|
|
12
|
+
* Used to reconstruct the result for an idempotent retry, so a re-sent clone
|
|
13
|
+
* returns the same option/unit ids a fresh response would (the agent needs them
|
|
14
|
+
* to continue authoring after the exact lost-response case idempotency covers).
|
|
15
|
+
*/
|
|
16
|
+
async function loadClonedOptions(tx, productId) {
|
|
17
|
+
const optionRows = await tx
|
|
18
|
+
.select({ id: productOptions.id })
|
|
19
|
+
.from(productOptions)
|
|
20
|
+
.where(eq(productOptions.productId, productId));
|
|
21
|
+
if (optionRows.length === 0)
|
|
22
|
+
return [];
|
|
23
|
+
const unitRows = await tx
|
|
24
|
+
.select({ id: optionUnits.id, optionId: optionUnits.optionId })
|
|
25
|
+
.from(optionUnits)
|
|
26
|
+
.where(inArray(optionUnits.optionId, optionRows.map((o) => o.id)));
|
|
27
|
+
return optionRows.map((o) => ({
|
|
28
|
+
id: o.id,
|
|
29
|
+
units: unitRows.filter((u) => u.optionId === o.id).map((u) => ({ id: u.id })),
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
function newContext(tx, sourceId, targetId, copyDepartures) {
|
|
33
|
+
return {
|
|
34
|
+
tx,
|
|
35
|
+
sourceId,
|
|
36
|
+
targetId,
|
|
37
|
+
copyDepartures,
|
|
38
|
+
optionIdMap: new Map(),
|
|
39
|
+
unitIdMap: new Map(),
|
|
40
|
+
unitsByNewOption: new Map(),
|
|
41
|
+
itineraryIdMap: new Map(),
|
|
42
|
+
dayIdMap: new Map(),
|
|
43
|
+
startTimeIdMap: new Map(),
|
|
44
|
+
ruleIdMap: new Map(),
|
|
45
|
+
slotIdMap: new Map(),
|
|
46
|
+
optionPriceRuleIdMap: new Map(),
|
|
47
|
+
optionUnitPriceRuleIdMap: new Map(),
|
|
48
|
+
pricingCategoryIdMap: new Map(),
|
|
49
|
+
productExtraIdMap: new Map(),
|
|
50
|
+
optionExtraConfigIdMap: new Map(),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Deep-clone a product graph as a draft (#1493): the new product row, then its
|
|
55
|
+
* content (options/units, pricing categories, itinerary, media, extras) and its
|
|
56
|
+
* pricing/operations — copied with correct id remapping. Availability is
|
|
57
|
+
* copied only when `copyDepartures`. MUST run inside the caller's transaction.
|
|
58
|
+
*/
|
|
59
|
+
async function cloneGraph(tx, sourceId, opts) {
|
|
60
|
+
const [sourceProduct] = await tx.select().from(products).where(eq(products.id, sourceId));
|
|
61
|
+
if (!sourceProduct)
|
|
62
|
+
return null;
|
|
63
|
+
const [targetProduct] = await tx
|
|
64
|
+
.insert(products)
|
|
65
|
+
.values({
|
|
66
|
+
...withoutSystemColumns(sourceProduct),
|
|
67
|
+
name: opts.name ?? copyName(sourceProduct.name),
|
|
68
|
+
status: opts.status ?? "draft",
|
|
69
|
+
...(opts.visibility ? { visibility: opts.visibility } : {}),
|
|
70
|
+
activated: false,
|
|
71
|
+
})
|
|
72
|
+
.returning();
|
|
73
|
+
if (!targetProduct) {
|
|
74
|
+
throw new Error("Failed to duplicate product");
|
|
75
|
+
}
|
|
76
|
+
const ctx = newContext(tx, sourceId, targetProduct.id, opts.copyDepartures);
|
|
77
|
+
await copyProductContent(ctx);
|
|
78
|
+
await copyPricingAndAvailability(ctx);
|
|
79
|
+
const options = [...ctx.optionIdMap.values()].map((id) => ({
|
|
80
|
+
id,
|
|
81
|
+
units: ctx.unitsByNewOption.get(id) ?? [],
|
|
82
|
+
}));
|
|
83
|
+
return { product: targetProduct, options };
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Deep-clone a product (#1493). Wraps {@link cloneGraph} with idempotency and an
|
|
87
|
+
* optional `product_versions` snapshot. The operator UI calls this with no
|
|
88
|
+
* overrides (full copy, `"{X} (Copy)"`, departures included); the agent passes
|
|
89
|
+
* `name` + `copyDepartures: false` + an `Idempotency-Key`.
|
|
90
|
+
*/
|
|
91
|
+
export async function cloneProduct(db, sourceProductId, options = {}) {
|
|
92
|
+
const [exists] = await db
|
|
93
|
+
.select({ id: products.id })
|
|
94
|
+
.from(products)
|
|
95
|
+
.where(eq(products.id, sourceProductId))
|
|
96
|
+
.limit(1);
|
|
97
|
+
if (!exists)
|
|
98
|
+
return { status: "not_found" };
|
|
99
|
+
const copyDepartures = options.copyDepartures ?? true;
|
|
100
|
+
const key = options.idempotencyKey;
|
|
101
|
+
const result = await db.transaction(async (tx) => {
|
|
102
|
+
if (key) {
|
|
103
|
+
// agent-quality: raw-sql reviewed -- owner: catalog-authoring; dynamic SQL interpolation uses Drizzle parameter binding or vetted SQL identifiers.
|
|
104
|
+
await tx.execute(sql `SELECT pg_advisory_xact_lock(hashtextextended(${key}, 0))`);
|
|
105
|
+
const [prev] = await tx
|
|
106
|
+
.select({ productId: productAuthoringRequests.productId })
|
|
107
|
+
.from(productAuthoringRequests)
|
|
108
|
+
.where(eq(productAuthoringRequests.idempotencyKey, key))
|
|
109
|
+
.limit(1);
|
|
110
|
+
if (prev) {
|
|
111
|
+
const [product] = await tx
|
|
112
|
+
.select()
|
|
113
|
+
.from(products)
|
|
114
|
+
.where(eq(products.id, prev.productId))
|
|
115
|
+
.limit(1);
|
|
116
|
+
if (product) {
|
|
117
|
+
return { product, options: await loadClonedOptions(tx, product.id), reused: true };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const cloned = await cloneGraph(tx, sourceProductId, {
|
|
122
|
+
name: options.name,
|
|
123
|
+
status: options.status,
|
|
124
|
+
visibility: options.visibility,
|
|
125
|
+
copyDepartures,
|
|
126
|
+
});
|
|
127
|
+
if (!cloned)
|
|
128
|
+
return null;
|
|
129
|
+
if (options.userId) {
|
|
130
|
+
await productsService.createVersion(tx, cloned.product.id, options.userId, {});
|
|
131
|
+
}
|
|
132
|
+
if (key) {
|
|
133
|
+
await tx
|
|
134
|
+
.insert(productAuthoringRequests)
|
|
135
|
+
.values({ idempotencyKey: key, productId: cloned.product.id, operation: "duplicate" });
|
|
136
|
+
}
|
|
137
|
+
return { ...cloned, reused: false };
|
|
138
|
+
});
|
|
139
|
+
if (!result)
|
|
140
|
+
return { status: "not_found" };
|
|
141
|
+
return { status: "ok", ...result };
|
|
142
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single, agent-recoverable problem. The shape is deliberately verbose so an
|
|
3
|
+
* LLM tool runtime can fix the spec and retry without a human: `field` says
|
|
4
|
+
* what to change, `message` says why, `fix` says how.
|
|
5
|
+
*/
|
|
6
|
+
export interface AuthoringIssue {
|
|
7
|
+
code: string;
|
|
8
|
+
field?: string;
|
|
9
|
+
message: string;
|
|
10
|
+
fix?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Thrown by the validator (and the builder, for structural problems it can only
|
|
14
|
+
* detect mid-build, e.g. an unresolved catalog). Routes translate this to a 422
|
|
15
|
+
* with `{ errors }` so the caller can self-correct.
|
|
16
|
+
*/
|
|
17
|
+
export declare class AuthoringValidationError extends Error {
|
|
18
|
+
readonly issues: AuthoringIssue[];
|
|
19
|
+
constructor(issues: AuthoringIssue[]);
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/authoring/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED;;;;GAIG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,MAAM,EAAE,cAAc,EAAE,CAAA;gBAErB,MAAM,EAAE,cAAc,EAAE;CAKrC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown by the validator (and the builder, for structural problems it can only
|
|
3
|
+
* detect mid-build, e.g. an unresolved catalog). Routes translate this to a 422
|
|
4
|
+
* with `{ errors }` so the caller can self-correct.
|
|
5
|
+
*/
|
|
6
|
+
export class AuthoringValidationError extends Error {
|
|
7
|
+
issues;
|
|
8
|
+
constructor(issues) {
|
|
9
|
+
super(issues.map((i) => i.message).join("; ") || "Invalid product graph");
|
|
10
|
+
this.name = "AuthoringValidationError";
|
|
11
|
+
this.issues = issues;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import type { EventBus } from "@voyant-travel/core";
|
|
2
|
+
import type { HonoExtension } from "@voyant-travel/hono/module";
|
|
3
|
+
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
4
|
+
/**
|
|
5
|
+
* Inventory authoring rides on the `products` admin prefix as a HonoExtension, so
|
|
6
|
+
* its routes land at `/v1/admin/products/...` while the implementation lives
|
|
7
|
+
* with the optional Inventory package. Same mechanism as
|
|
8
|
+
* `bookingsSupplierExtension`.
|
|
9
|
+
*
|
|
10
|
+
* POST /v1/admin/products/{id}/duplicate — deep-clone a product graph (#1493)
|
|
11
|
+
* POST /v1/admin/products/compose — build a new product graph from a spec (#1495)
|
|
12
|
+
*
|
|
13
|
+
* The duplicate route is the canonical product clone (it replaces the operator
|
|
14
|
+
* template's previous local `duplicateProductAsDraft` route). No body → a full
|
|
15
|
+
* copy named `"{X} (Copy)"` with departures (preserves the UI). The agent passes
|
|
16
|
+
* `{ name, copyDepartures: false }` + an `Idempotency-Key`.
|
|
17
|
+
*/
|
|
18
|
+
type Env = {
|
|
19
|
+
Variables: {
|
|
20
|
+
db: PostgresJsDatabase;
|
|
21
|
+
userId?: string;
|
|
22
|
+
eventBus?: EventBus;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export declare const inventoryAuthoringRoutes: import("hono/hono-base").HonoBase<Env, {
|
|
26
|
+
"/compose": {
|
|
27
|
+
$post: {
|
|
28
|
+
input: {};
|
|
29
|
+
output: {
|
|
30
|
+
error: string;
|
|
31
|
+
issues: {
|
|
32
|
+
code: string;
|
|
33
|
+
field?: string | undefined;
|
|
34
|
+
message: string;
|
|
35
|
+
fix?: string | undefined;
|
|
36
|
+
}[];
|
|
37
|
+
};
|
|
38
|
+
outputFormat: "json";
|
|
39
|
+
status: 422;
|
|
40
|
+
} | {
|
|
41
|
+
input: {};
|
|
42
|
+
output: {
|
|
43
|
+
data: {
|
|
44
|
+
id: string;
|
|
45
|
+
options: {
|
|
46
|
+
ref: string;
|
|
47
|
+
id: string;
|
|
48
|
+
units: {
|
|
49
|
+
ref: string;
|
|
50
|
+
id: string;
|
|
51
|
+
}[];
|
|
52
|
+
}[];
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
outputFormat: "json";
|
|
56
|
+
status: 200 | 201;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
} & {
|
|
60
|
+
"/:id/duplicate": {
|
|
61
|
+
$post: {
|
|
62
|
+
input: {
|
|
63
|
+
param: {
|
|
64
|
+
id: string;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
output: {
|
|
68
|
+
error: string;
|
|
69
|
+
};
|
|
70
|
+
outputFormat: "json";
|
|
71
|
+
status: 400;
|
|
72
|
+
} | {
|
|
73
|
+
input: {
|
|
74
|
+
param: {
|
|
75
|
+
id: string;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
output: {
|
|
79
|
+
error: string;
|
|
80
|
+
};
|
|
81
|
+
outputFormat: "json";
|
|
82
|
+
status: 404;
|
|
83
|
+
} | {
|
|
84
|
+
input: {
|
|
85
|
+
param: {
|
|
86
|
+
id: string;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
output: {
|
|
90
|
+
data: {
|
|
91
|
+
id: string;
|
|
92
|
+
name: string;
|
|
93
|
+
status: "draft" | "active" | "archived";
|
|
94
|
+
description: string | null;
|
|
95
|
+
inclusionsHtml: string | null;
|
|
96
|
+
exclusionsHtml: string | null;
|
|
97
|
+
termsHtml: string | null;
|
|
98
|
+
termsShowOnContract: boolean;
|
|
99
|
+
bookingMode: "date" | "date_time" | "open" | "stay" | "transfer" | "itinerary" | "other";
|
|
100
|
+
capacityMode: "free_sale" | "limited" | "on_request";
|
|
101
|
+
timezone: string | null;
|
|
102
|
+
defaultLanguageTag: string | null;
|
|
103
|
+
visibility: "public" | "private" | "hidden";
|
|
104
|
+
activated: boolean;
|
|
105
|
+
reservationTimeoutMinutes: number | null;
|
|
106
|
+
sellCurrency: string;
|
|
107
|
+
sellAmountCents: number | null;
|
|
108
|
+
costAmountCents: number | null;
|
|
109
|
+
marginPercent: number | null;
|
|
110
|
+
facilityId: string | null;
|
|
111
|
+
supplierId: string | null;
|
|
112
|
+
startDate: string | null;
|
|
113
|
+
endDate: string | null;
|
|
114
|
+
pax: number | null;
|
|
115
|
+
productTypeId: string | null;
|
|
116
|
+
contractTemplateId: string | null;
|
|
117
|
+
taxClassId: string | null;
|
|
118
|
+
customerPaymentPolicy: import("hono/utils/types").JSONValue;
|
|
119
|
+
tags: string[] | null;
|
|
120
|
+
createdAt: string;
|
|
121
|
+
updatedAt: string;
|
|
122
|
+
};
|
|
123
|
+
options: {
|
|
124
|
+
id: string;
|
|
125
|
+
units: {
|
|
126
|
+
id: string;
|
|
127
|
+
}[];
|
|
128
|
+
}[];
|
|
129
|
+
};
|
|
130
|
+
outputFormat: "json";
|
|
131
|
+
status: 200 | 201;
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
}, "/", "/:id/duplicate">;
|
|
135
|
+
export declare const inventoryAuthoringExtension: HonoExtension;
|
|
136
|
+
export declare const catalogAuthoringRoutes: import("hono/hono-base").HonoBase<Env, {
|
|
137
|
+
"/compose": {
|
|
138
|
+
$post: {
|
|
139
|
+
input: {};
|
|
140
|
+
output: {
|
|
141
|
+
error: string;
|
|
142
|
+
issues: {
|
|
143
|
+
code: string;
|
|
144
|
+
field?: string | undefined;
|
|
145
|
+
message: string;
|
|
146
|
+
fix?: string | undefined;
|
|
147
|
+
}[];
|
|
148
|
+
};
|
|
149
|
+
outputFormat: "json";
|
|
150
|
+
status: 422;
|
|
151
|
+
} | {
|
|
152
|
+
input: {};
|
|
153
|
+
output: {
|
|
154
|
+
data: {
|
|
155
|
+
id: string;
|
|
156
|
+
options: {
|
|
157
|
+
ref: string;
|
|
158
|
+
id: string;
|
|
159
|
+
units: {
|
|
160
|
+
ref: string;
|
|
161
|
+
id: string;
|
|
162
|
+
}[];
|
|
163
|
+
}[];
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
outputFormat: "json";
|
|
167
|
+
status: 200 | 201;
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
} & {
|
|
171
|
+
"/:id/duplicate": {
|
|
172
|
+
$post: {
|
|
173
|
+
input: {
|
|
174
|
+
param: {
|
|
175
|
+
id: string;
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
output: {
|
|
179
|
+
error: string;
|
|
180
|
+
};
|
|
181
|
+
outputFormat: "json";
|
|
182
|
+
status: 400;
|
|
183
|
+
} | {
|
|
184
|
+
input: {
|
|
185
|
+
param: {
|
|
186
|
+
id: string;
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
output: {
|
|
190
|
+
error: string;
|
|
191
|
+
};
|
|
192
|
+
outputFormat: "json";
|
|
193
|
+
status: 404;
|
|
194
|
+
} | {
|
|
195
|
+
input: {
|
|
196
|
+
param: {
|
|
197
|
+
id: string;
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
output: {
|
|
201
|
+
data: {
|
|
202
|
+
id: string;
|
|
203
|
+
name: string;
|
|
204
|
+
status: "draft" | "active" | "archived";
|
|
205
|
+
description: string | null;
|
|
206
|
+
inclusionsHtml: string | null;
|
|
207
|
+
exclusionsHtml: string | null;
|
|
208
|
+
termsHtml: string | null;
|
|
209
|
+
termsShowOnContract: boolean;
|
|
210
|
+
bookingMode: "date" | "date_time" | "open" | "stay" | "transfer" | "itinerary" | "other";
|
|
211
|
+
capacityMode: "free_sale" | "limited" | "on_request";
|
|
212
|
+
timezone: string | null;
|
|
213
|
+
defaultLanguageTag: string | null;
|
|
214
|
+
visibility: "public" | "private" | "hidden";
|
|
215
|
+
activated: boolean;
|
|
216
|
+
reservationTimeoutMinutes: number | null;
|
|
217
|
+
sellCurrency: string;
|
|
218
|
+
sellAmountCents: number | null;
|
|
219
|
+
costAmountCents: number | null;
|
|
220
|
+
marginPercent: number | null;
|
|
221
|
+
facilityId: string | null;
|
|
222
|
+
supplierId: string | null;
|
|
223
|
+
startDate: string | null;
|
|
224
|
+
endDate: string | null;
|
|
225
|
+
pax: number | null;
|
|
226
|
+
productTypeId: string | null;
|
|
227
|
+
contractTemplateId: string | null;
|
|
228
|
+
taxClassId: string | null;
|
|
229
|
+
customerPaymentPolicy: import("hono/utils/types").JSONValue;
|
|
230
|
+
tags: string[] | null;
|
|
231
|
+
createdAt: string;
|
|
232
|
+
updatedAt: string;
|
|
233
|
+
};
|
|
234
|
+
options: {
|
|
235
|
+
id: string;
|
|
236
|
+
units: {
|
|
237
|
+
id: string;
|
|
238
|
+
}[];
|
|
239
|
+
}[];
|
|
240
|
+
};
|
|
241
|
+
outputFormat: "json";
|
|
242
|
+
status: 200 | 201;
|
|
243
|
+
};
|
|
244
|
+
};
|
|
245
|
+
}, "/", "/:id/duplicate">;
|
|
246
|
+
export declare const catalogAuthoringExtension: HonoExtension;
|
|
247
|
+
export {};
|
|
248
|
+
//# sourceMappingURL=extension.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../../src/authoring/extension.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAa,MAAM,qBAAqB,CAAA;AAE9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAcjE;;;;;;;;;;;;;GAaG;AAEH,KAAK,GAAG,GAAG;IACT,SAAS,EAAE;QACT,EAAE,EAAE,kBAAkB,CAAA;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,QAAQ,CAAC,EAAE,QAAQ,CAAA;KACpB,CAAA;CACF,CAAA;AAmDD,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBA6DjC,CAAA;AAYJ,eAAO,MAAM,2BAA2B,EAAE,aAGzC,CAAA;AAED,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAA2B,CAAA;AAE9D,eAAO,MAAM,yBAAyB,EAAE,aAMvC,CAAA"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { parseJsonBody } from "@voyant-travel/hono";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { appendProductMutationLedgerEntry, } from "../action-ledger.js";
|
|
5
|
+
import { emitProductContentChanged } from "../events.js";
|
|
6
|
+
import { productStatusEnum, productVisibilityEnum } from "../schema.js";
|
|
7
|
+
import { cloneProduct } from "./clone.js";
|
|
8
|
+
import { composeProduct } from "./service.js";
|
|
9
|
+
import { productGraphSpecSchema } from "./spec.js";
|
|
10
|
+
const composeBodySchema = z.object({
|
|
11
|
+
spec: productGraphSpecSchema,
|
|
12
|
+
idempotencyKey: z.string().min(1).max(255).optional(),
|
|
13
|
+
});
|
|
14
|
+
const duplicateBodySchema = z.object({
|
|
15
|
+
name: z.string().min(1).max(255).optional(),
|
|
16
|
+
status: z.enum(productStatusEnum.enumValues).optional(),
|
|
17
|
+
visibility: z.enum(productVisibilityEnum.enumValues).optional(),
|
|
18
|
+
copyDepartures: z.boolean().optional(),
|
|
19
|
+
idempotencyKey: z.string().min(1).max(255).optional(),
|
|
20
|
+
});
|
|
21
|
+
/** Header takes precedence over a body-supplied key. */
|
|
22
|
+
function idempotencyKey(c, bodyKey) {
|
|
23
|
+
return c.req.header("Idempotency-Key") ?? bodyKey;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Records the same action-ledger entry + `product.content.changed` event the
|
|
27
|
+
* granular product routes emit, so an authored product is indexed and audited
|
|
28
|
+
* like any other create. Only called for freshly built products (a reused
|
|
29
|
+
* idempotent response created nothing new).
|
|
30
|
+
*/
|
|
31
|
+
async function recordAuthoring(
|
|
32
|
+
// biome-ignore lint/suspicious/noExplicitAny: bridges this extension's Env to products' ledger Context<Env> (#1493/#1495); cast to LedgerContext below
|
|
33
|
+
c, action, productId) {
|
|
34
|
+
const verb = action === "duplicate" ? "duplicate" : "compose";
|
|
35
|
+
await appendProductMutationLedgerEntry(c, {
|
|
36
|
+
action,
|
|
37
|
+
productId,
|
|
38
|
+
changedFields: [],
|
|
39
|
+
subject: "product",
|
|
40
|
+
actionName: `product.${verb}`,
|
|
41
|
+
routeOrToolName: `products.${verb}`,
|
|
42
|
+
});
|
|
43
|
+
await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "product" });
|
|
44
|
+
}
|
|
45
|
+
export const inventoryAuthoringRoutes = new Hono()
|
|
46
|
+
.post("/compose", async (c) => {
|
|
47
|
+
const body = await parseJsonBody(c, composeBodySchema);
|
|
48
|
+
const outcome = await composeProduct(c.get("db"), body.spec, {
|
|
49
|
+
userId: c.get("userId"),
|
|
50
|
+
idempotencyKey: idempotencyKey(c, body.idempotencyKey),
|
|
51
|
+
});
|
|
52
|
+
if (outcome.status === "invalid") {
|
|
53
|
+
return c.json({ error: "invalid_product_graph", issues: outcome.issues }, 422);
|
|
54
|
+
}
|
|
55
|
+
if (!outcome.reused) {
|
|
56
|
+
await recordAuthoring(c, "create", outcome.result.productId);
|
|
57
|
+
}
|
|
58
|
+
return c.json({ data: { id: outcome.result.productId, options: outcome.result.options } }, outcome.reused ? 200 : 201);
|
|
59
|
+
})
|
|
60
|
+
.post("/:id/duplicate", async (c) => {
|
|
61
|
+
// The UI clones with no body; the agent sends overrides. Tolerate both.
|
|
62
|
+
const raw = (await c.req.text()).trim();
|
|
63
|
+
let body = {};
|
|
64
|
+
if (raw.length > 0) {
|
|
65
|
+
let json;
|
|
66
|
+
try {
|
|
67
|
+
json = JSON.parse(raw);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return c.json({ error: "Invalid JSON body" }, 400);
|
|
71
|
+
}
|
|
72
|
+
const parsed = duplicateBodySchema.safeParse(json);
|
|
73
|
+
if (!parsed.success) {
|
|
74
|
+
return c.json({ error: "Invalid body", issues: parsed.error.issues }, 400);
|
|
75
|
+
}
|
|
76
|
+
body = parsed.data;
|
|
77
|
+
}
|
|
78
|
+
const outcome = await cloneProduct(c.get("db"), c.req.param("id"), {
|
|
79
|
+
name: body.name,
|
|
80
|
+
status: body.status,
|
|
81
|
+
visibility: body.visibility,
|
|
82
|
+
copyDepartures: body.copyDepartures,
|
|
83
|
+
userId: c.get("userId"),
|
|
84
|
+
idempotencyKey: idempotencyKey(c, body.idempotencyKey),
|
|
85
|
+
});
|
|
86
|
+
if (outcome.status === "not_found") {
|
|
87
|
+
return c.json({ error: "Product not found" }, 404);
|
|
88
|
+
}
|
|
89
|
+
if (!outcome.reused) {
|
|
90
|
+
await recordAuthoring(c, "duplicate", outcome.product.id);
|
|
91
|
+
}
|
|
92
|
+
// `data` stays the full product row (the UI reads `data.id`); `options`
|
|
93
|
+
// carries the cloned option/unit ids for agent follow-up calls.
|
|
94
|
+
return c.json({ data: outcome.product, options: outcome.options }, outcome.reused ? 200 : 201);
|
|
95
|
+
});
|
|
96
|
+
const inventoryAuthoringExtensionDef = {
|
|
97
|
+
name: "inventory-authoring",
|
|
98
|
+
module: "products",
|
|
99
|
+
// The compose + duplicate routes run interactive transactions
|
|
100
|
+
// (atomic clone of the product graph). This extension mounts under
|
|
101
|
+
// /v1/admin/products, so the flag upgrades that surface to the
|
|
102
|
+
// transaction-capable db client in deployments that split factories.
|
|
103
|
+
requiresTransactionalDb: true,
|
|
104
|
+
};
|
|
105
|
+
export const inventoryAuthoringExtension = {
|
|
106
|
+
extension: inventoryAuthoringExtensionDef,
|
|
107
|
+
adminRoutes: inventoryAuthoringRoutes,
|
|
108
|
+
};
|
|
109
|
+
export const catalogAuthoringRoutes = inventoryAuthoringRoutes;
|
|
110
|
+
export const catalogAuthoringExtension = {
|
|
111
|
+
extension: {
|
|
112
|
+
...inventoryAuthoringExtensionDef,
|
|
113
|
+
name: "catalog-authoring",
|
|
114
|
+
},
|
|
115
|
+
adminRoutes: inventoryAuthoringRoutes,
|
|
116
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Module } from "@voyant-travel/core";
|
|
2
|
+
export declare const inventoryAuthoringModule: Module;
|
|
3
|
+
export { type BuildProductGraphOptions, type BuildProductGraphResult, buildProductGraph, } from "./builder.js";
|
|
4
|
+
export { type ClonedOption, type CloneProductOptions, type CloneProductOutcome, cloneProduct, } from "./clone.js";
|
|
5
|
+
export { type AuthoringIssue, AuthoringValidationError } from "./errors.js";
|
|
6
|
+
export { catalogAuthoringExtension, catalogAuthoringRoutes, inventoryAuthoringExtension, inventoryAuthoringRoutes, } from "./extension.js";
|
|
7
|
+
export type { NewProductAuthoringRequest, ProductAuthoringRequest } from "./schema.js";
|
|
8
|
+
export { productAuthoringRequests } from "./schema.js";
|
|
9
|
+
export { type AuthoringRunOptions, type ComposeProductOutcome, composeProduct, } from "./service.js";
|
|
10
|
+
export { type ProductGraphSpec, productGraphSpecSchema } from "./spec.js";
|
|
11
|
+
export { validateProductGraph } from "./validate.js";
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/authoring/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAEjD,eAAO,MAAM,wBAAwB,EAAE,MAEtC,CAAA;AAED,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,iBAAiB,GAClB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,YAAY,GACb,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,KAAK,cAAc,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAC3E,OAAO,EACL,yBAAyB,EACzB,sBAAsB,EACtB,2BAA2B,EAC3B,wBAAwB,GACzB,MAAM,gBAAgB,CAAA;AACvB,YAAY,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACtF,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,cAAc,GACf,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,KAAK,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAA;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const inventoryAuthoringModule = {
|
|
2
|
+
name: "inventory-authoring",
|
|
3
|
+
};
|
|
4
|
+
export { buildProductGraph, } from "./builder.js";
|
|
5
|
+
export { cloneProduct, } from "./clone.js";
|
|
6
|
+
export { AuthoringValidationError } from "./errors.js";
|
|
7
|
+
export { catalogAuthoringExtension, catalogAuthoringRoutes, inventoryAuthoringExtension, inventoryAuthoringRoutes, } from "./extension.js";
|
|
8
|
+
export { productAuthoringRequests } from "./schema.js";
|
|
9
|
+
export { composeProduct, } from "./service.js";
|
|
10
|
+
export { productGraphSpecSchema } from "./spec.js";
|
|
11
|
+
export { validateProductGraph } from "./validate.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/authoring/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"}
|