@voyantjs/products 0.30.6 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/catalog-policy.d.ts.map +1 -1
- package/dist/catalog-policy.js +126 -0
- package/dist/routes.d.ts +5 -5
- package/dist/service-catalog-plane.d.ts +6 -0
- package/dist/service-catalog-plane.d.ts.map +1 -1
- package/dist/service-catalog-plane.js +116 -1
- package/dist/service.d.ts +5 -5
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog-policy.d.ts","sourceRoot":"","sources":["../src/catalog-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAErF;;;GAGG;AACH,QAAA,MAAM,oBAAoB,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"catalog-policy.d.ts","sourceRoot":"","sources":["../src/catalog-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAErF;;;GAGG;AACH,QAAA,MAAM,oBAAoB,EAAE,gBAAgB,EAwgB3C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,2CAA0C,CAAA;AAE3E,OAAO,EAAE,oBAAoB,EAAE,CAAA"}
|
package/dist/catalog-policy.js
CHANGED
|
@@ -144,6 +144,34 @@ const PRODUCT_FIELD_POLICY = [
|
|
|
144
144
|
overrideFriction: "none",
|
|
145
145
|
sourceFreshness: "sync",
|
|
146
146
|
},
|
|
147
|
+
{
|
|
148
|
+
path: "shortDescription",
|
|
149
|
+
class: "merchandisable",
|
|
150
|
+
merge: "replace",
|
|
151
|
+
drift: "low",
|
|
152
|
+
reindex: "entry-locale",
|
|
153
|
+
snapshot: "on-book",
|
|
154
|
+
query: "indexed-column",
|
|
155
|
+
localized: true,
|
|
156
|
+
visibility: ["staff", "customer", "partner"],
|
|
157
|
+
editRole: "marketing",
|
|
158
|
+
overrideFriction: "none",
|
|
159
|
+
sourceFreshness: "sync",
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
path: "slug",
|
|
163
|
+
class: "merchandisable",
|
|
164
|
+
merge: "replace",
|
|
165
|
+
drift: "medium",
|
|
166
|
+
reindex: "entry-locale",
|
|
167
|
+
snapshot: "on-book",
|
|
168
|
+
query: "indexed-column",
|
|
169
|
+
localized: true,
|
|
170
|
+
visibility: ["staff", "customer", "partner"],
|
|
171
|
+
editRole: "marketing",
|
|
172
|
+
overrideFriction: "none",
|
|
173
|
+
sourceFreshness: "sync",
|
|
174
|
+
},
|
|
147
175
|
// TODO(catalog): split into marketing_tags + facet_tags per architecture
|
|
148
176
|
// §7.1 (human-readable + machine-evaluable rule). Today's schema has one
|
|
149
177
|
// `tags` column; declared here as merchandisable + additive-set so
|
|
@@ -321,6 +349,104 @@ const PRODUCT_FIELD_POLICY = [
|
|
|
321
349
|
overrideFriction: "none",
|
|
322
350
|
sourceFreshness: "sync",
|
|
323
351
|
},
|
|
352
|
+
{
|
|
353
|
+
path: "startDateEpochDays",
|
|
354
|
+
class: "structural",
|
|
355
|
+
merge: "source-only",
|
|
356
|
+
drift: "medium",
|
|
357
|
+
reindex: "facet-affecting",
|
|
358
|
+
snapshot: "on-book",
|
|
359
|
+
query: "indexed-column",
|
|
360
|
+
localized: false,
|
|
361
|
+
visibility: ["staff", "customer", "partner"],
|
|
362
|
+
editRole: "none",
|
|
363
|
+
overrideFriction: "none",
|
|
364
|
+
sourceFreshness: "sync",
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
path: "endDateEpochDays",
|
|
368
|
+
class: "structural",
|
|
369
|
+
merge: "source-only",
|
|
370
|
+
drift: "medium",
|
|
371
|
+
reindex: "facet-affecting",
|
|
372
|
+
snapshot: "on-book",
|
|
373
|
+
query: "indexed-column",
|
|
374
|
+
localized: false,
|
|
375
|
+
visibility: ["staff", "customer", "partner"],
|
|
376
|
+
editRole: "none",
|
|
377
|
+
overrideFriction: "none",
|
|
378
|
+
sourceFreshness: "sync",
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
path: "durationDays",
|
|
382
|
+
class: "structural",
|
|
383
|
+
merge: "source-only",
|
|
384
|
+
drift: "low",
|
|
385
|
+
reindex: "facet-affecting",
|
|
386
|
+
snapshot: "on-book",
|
|
387
|
+
query: "indexed-column",
|
|
388
|
+
localized: false,
|
|
389
|
+
visibility: ["staff", "customer", "partner"],
|
|
390
|
+
editRole: "none",
|
|
391
|
+
overrideFriction: "none",
|
|
392
|
+
sourceFreshness: "sync",
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
path: "primaryMediaUrl",
|
|
396
|
+
class: "merchandisable",
|
|
397
|
+
merge: "source-only",
|
|
398
|
+
drift: "low",
|
|
399
|
+
reindex: "entry",
|
|
400
|
+
snapshot: "on-book",
|
|
401
|
+
query: "indexed-column",
|
|
402
|
+
localized: false,
|
|
403
|
+
visibility: ["staff", "customer", "partner"],
|
|
404
|
+
editRole: "none",
|
|
405
|
+
overrideFriction: "none",
|
|
406
|
+
sourceFreshness: "sync",
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
path: "coverMediaUrl",
|
|
410
|
+
class: "merchandisable",
|
|
411
|
+
merge: "source-only",
|
|
412
|
+
drift: "low",
|
|
413
|
+
reindex: "entry",
|
|
414
|
+
snapshot: "on-book",
|
|
415
|
+
query: "indexed-column",
|
|
416
|
+
localized: false,
|
|
417
|
+
visibility: ["staff", "customer", "partner"],
|
|
418
|
+
editRole: "none",
|
|
419
|
+
overrideFriction: "none",
|
|
420
|
+
sourceFreshness: "sync",
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
path: "latitude",
|
|
424
|
+
class: "structural",
|
|
425
|
+
merge: "source-only",
|
|
426
|
+
drift: "low",
|
|
427
|
+
reindex: "facet-affecting",
|
|
428
|
+
snapshot: "on-book",
|
|
429
|
+
query: "indexed-column",
|
|
430
|
+
localized: false,
|
|
431
|
+
visibility: ["staff", "customer", "partner"],
|
|
432
|
+
editRole: "none",
|
|
433
|
+
overrideFriction: "none",
|
|
434
|
+
sourceFreshness: "sync",
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
path: "longitude",
|
|
438
|
+
class: "structural",
|
|
439
|
+
merge: "source-only",
|
|
440
|
+
drift: "low",
|
|
441
|
+
reindex: "facet-affecting",
|
|
442
|
+
snapshot: "on-book",
|
|
443
|
+
query: "indexed-column",
|
|
444
|
+
localized: false,
|
|
445
|
+
visibility: ["staff", "customer", "partner"],
|
|
446
|
+
editRole: "none",
|
|
447
|
+
overrideFriction: "none",
|
|
448
|
+
sourceFreshness: "sync",
|
|
449
|
+
},
|
|
324
450
|
{
|
|
325
451
|
path: "timezone",
|
|
326
452
|
class: "managed",
|
package/dist/routes.d.ts
CHANGED
|
@@ -1278,13 +1278,13 @@ export declare const productRoutes: import("hono/hono-base").HonoBase<Env, {
|
|
|
1278
1278
|
updatedAt: string;
|
|
1279
1279
|
productId: string;
|
|
1280
1280
|
title: string;
|
|
1281
|
+
latitude: number | null;
|
|
1282
|
+
longitude: number | null;
|
|
1281
1283
|
city: string | null;
|
|
1282
1284
|
address: string | null;
|
|
1283
1285
|
sortOrder: number;
|
|
1284
1286
|
countryCode: string | null;
|
|
1285
1287
|
locationType: "start" | "other" | "end" | "meeting_point" | "pickup" | "dropoff" | "point_of_interest";
|
|
1286
|
-
latitude: number | null;
|
|
1287
|
-
longitude: number | null;
|
|
1288
1288
|
googlePlaceId: string | null;
|
|
1289
1289
|
applePlaceId: string | null;
|
|
1290
1290
|
tripadvisorLocationId: string | null;
|
|
@@ -2433,8 +2433,8 @@ export declare const productRoutes: import("hono/hono-base").HonoBase<Env, {
|
|
|
2433
2433
|
slug: string | null;
|
|
2434
2434
|
description: string | null;
|
|
2435
2435
|
productId: string;
|
|
2436
|
-
languageTag: string;
|
|
2437
2436
|
shortDescription: string | null;
|
|
2437
|
+
languageTag: string;
|
|
2438
2438
|
seoTitle: string | null;
|
|
2439
2439
|
seoDescription: string | null;
|
|
2440
2440
|
};
|
|
@@ -2592,8 +2592,8 @@ export declare const productRoutes: import("hono/hono-base").HonoBase<Env, {
|
|
|
2592
2592
|
updatedAt: string;
|
|
2593
2593
|
description: string | null;
|
|
2594
2594
|
optionId: string;
|
|
2595
|
-
languageTag: string;
|
|
2596
2595
|
shortDescription: string | null;
|
|
2596
|
+
languageTag: string;
|
|
2597
2597
|
};
|
|
2598
2598
|
};
|
|
2599
2599
|
outputFormat: "json";
|
|
@@ -2746,8 +2746,8 @@ export declare const productRoutes: import("hono/hono-base").HonoBase<Env, {
|
|
|
2746
2746
|
updatedAt: string;
|
|
2747
2747
|
description: string | null;
|
|
2748
2748
|
unitId: string;
|
|
2749
|
-
languageTag: string;
|
|
2750
2749
|
shortDescription: string | null;
|
|
2750
|
+
languageTag: string;
|
|
2751
2751
|
};
|
|
2752
2752
|
};
|
|
2753
2753
|
outputFormat: "json";
|
|
@@ -167,6 +167,12 @@ export declare function createProductDocumentBuilder(db: AnyDrizzleDb, context:
|
|
|
167
167
|
extensions?: ReadonlyArray<ProductProjectionExtension>;
|
|
168
168
|
registry?: FieldPolicyRegistry;
|
|
169
169
|
}): DocumentBuilder;
|
|
170
|
+
/**
|
|
171
|
+
* Product-owned storefront-card projection. This extension keeps the
|
|
172
|
+
* customer catalog slice directly renderable by denormalizing localized
|
|
173
|
+
* routing, card media, duration, and map coordinates into the search doc.
|
|
174
|
+
*/
|
|
175
|
+
export declare function createProductStorefrontCardProjectionExtension(): ProductProjectionExtension;
|
|
170
176
|
/**
|
|
171
177
|
* Re-exports for routes that only import from this file.
|
|
172
178
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service-catalog-plane.d.ts","sourceRoot":"","sources":["../src/service-catalog-plane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAGL,KAAK,oBAAoB,EAEzB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,aAAa,EAElB,KAAK,UAAU,EAChB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAIhD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"service-catalog-plane.d.ts","sourceRoot":"","sources":["../src/service-catalog-plane.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,9 +19,11 @@
|
|
|
19
19
|
* in their own service-catalog-plane.ts files).
|
|
20
20
|
*/
|
|
21
21
|
import { buildIndexerDocument, buildSnapshotInputFromView, createFieldPolicyRegistry, resolveEntityView, } from "@voyantjs/catalog";
|
|
22
|
-
import { eq } from "drizzle-orm";
|
|
22
|
+
import { asc, eq } from "drizzle-orm";
|
|
23
23
|
import { productCatalogPolicy } from "./catalog-policy.js";
|
|
24
24
|
import { products } from "./schema-core.js";
|
|
25
|
+
import { productDays, productItineraries, productMedia } from "./schema-itinerary.js";
|
|
26
|
+
import { productLocations, productTranslations } from "./schema-settings.js";
|
|
25
27
|
/**
|
|
26
28
|
* Lazy-initialized registry. Built once per process; the field-policy file
|
|
27
29
|
* is static so this is safe to memoize.
|
|
@@ -69,6 +71,8 @@ export function productRowToProjection(row, context) {
|
|
|
69
71
|
["pax", row.pax],
|
|
70
72
|
["startDate", row.startDate],
|
|
71
73
|
["endDate", row.endDate],
|
|
74
|
+
["startDateEpochDays", dateToEpochDays(row.startDate)],
|
|
75
|
+
["endDateEpochDays", dateToEpochDays(row.endDate)],
|
|
72
76
|
["timezone", row.timezone],
|
|
73
77
|
["reservationTimeoutMinutes", row.reservationTimeoutMinutes],
|
|
74
78
|
// Pricing (configured defaults — quote-time prices come from pricing module)
|
|
@@ -206,6 +210,17 @@ export function createProductDocumentEmitter(context) {
|
|
|
206
210
|
},
|
|
207
211
|
};
|
|
208
212
|
}
|
|
213
|
+
function isPublicStorefrontProduct(row) {
|
|
214
|
+
return row.status === "active" && row.activated === true && row.visibility === "public";
|
|
215
|
+
}
|
|
216
|
+
function shouldEmitForSlice(row, slice) {
|
|
217
|
+
if (slice.audience === "customer" ||
|
|
218
|
+
slice.audience === "partner" ||
|
|
219
|
+
slice.audience === "supplier") {
|
|
220
|
+
return isPublicStorefrontProduct(row);
|
|
221
|
+
}
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
209
224
|
/**
|
|
210
225
|
* Async `DocumentBuilder` for products — fetches the row by id, then emits.
|
|
211
226
|
* Plug this into `IndexerService.reindexEntity` for live reindex events.
|
|
@@ -230,6 +245,8 @@ export function createProductDocumentBuilder(db, context) {
|
|
|
230
245
|
const row = rows[0];
|
|
231
246
|
if (!row)
|
|
232
247
|
return null;
|
|
248
|
+
if (!shouldEmitForSlice(row, slice))
|
|
249
|
+
return null;
|
|
233
250
|
const baseProjection = productRowToProjection(row, {
|
|
234
251
|
sellerOperatorId: context.sellerOperatorId,
|
|
235
252
|
});
|
|
@@ -246,3 +263,101 @@ export function createProductDocumentBuilder(db, context) {
|
|
|
246
263
|
return buildIndexerDocument(registry, merged, slice, entityId);
|
|
247
264
|
};
|
|
248
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* Product-owned storefront-card projection. This extension keeps the
|
|
268
|
+
* customer catalog slice directly renderable by denormalizing localized
|
|
269
|
+
* routing, card media, duration, and map coordinates into the search doc.
|
|
270
|
+
*/
|
|
271
|
+
export function createProductStorefrontCardProjectionExtension() {
|
|
272
|
+
return {
|
|
273
|
+
name: "products:storefront-card",
|
|
274
|
+
async project(db, productId, slice) {
|
|
275
|
+
const [translations, mediaRows, locationRows, itineraryRows] = await Promise.all([
|
|
276
|
+
db
|
|
277
|
+
.select({
|
|
278
|
+
languageTag: productTranslations.languageTag,
|
|
279
|
+
name: productTranslations.name,
|
|
280
|
+
slug: productTranslations.slug,
|
|
281
|
+
shortDescription: productTranslations.shortDescription,
|
|
282
|
+
})
|
|
283
|
+
.from(productTranslations)
|
|
284
|
+
.where(eq(productTranslations.productId, productId))
|
|
285
|
+
.orderBy(asc(productTranslations.updatedAt)),
|
|
286
|
+
db
|
|
287
|
+
.select({
|
|
288
|
+
url: productMedia.url,
|
|
289
|
+
isCover: productMedia.isCover,
|
|
290
|
+
isBrochure: productMedia.isBrochure,
|
|
291
|
+
sortOrder: productMedia.sortOrder,
|
|
292
|
+
createdAt: productMedia.createdAt,
|
|
293
|
+
})
|
|
294
|
+
.from(productMedia)
|
|
295
|
+
.where(eq(productMedia.productId, productId))
|
|
296
|
+
.orderBy(asc(productMedia.sortOrder), asc(productMedia.createdAt)),
|
|
297
|
+
db
|
|
298
|
+
.select({
|
|
299
|
+
latitude: productLocations.latitude,
|
|
300
|
+
longitude: productLocations.longitude,
|
|
301
|
+
sortOrder: productLocations.sortOrder,
|
|
302
|
+
createdAt: productLocations.createdAt,
|
|
303
|
+
})
|
|
304
|
+
.from(productLocations)
|
|
305
|
+
.where(eq(productLocations.productId, productId))
|
|
306
|
+
.orderBy(asc(productLocations.sortOrder), asc(productLocations.createdAt)),
|
|
307
|
+
db
|
|
308
|
+
.select({ id: productItineraries.id, isDefault: productItineraries.isDefault })
|
|
309
|
+
.from(productItineraries)
|
|
310
|
+
.where(eq(productItineraries.productId, productId))
|
|
311
|
+
.orderBy(asc(productItineraries.sortOrder)),
|
|
312
|
+
]);
|
|
313
|
+
const translation = pickTranslation(translations, slice.locale);
|
|
314
|
+
const cover = mediaRows.filter((m) => !m.isBrochure).find((m) => m.isCover);
|
|
315
|
+
const primaryMedia = cover ?? mediaRows.find((m) => !m.isBrochure) ?? null;
|
|
316
|
+
const coordinateLocation = locationRows.find((l) => l.latitude != null && l.longitude != null) ?? null;
|
|
317
|
+
const defaultItinerary = itineraryRows.find((it) => it.isDefault) ?? itineraryRows[0];
|
|
318
|
+
const durationDays = defaultItinerary
|
|
319
|
+
? await estimateItineraryDurationDays(db, defaultItinerary.id)
|
|
320
|
+
: null;
|
|
321
|
+
const out = new Map([
|
|
322
|
+
["slug", translation?.slug ?? null],
|
|
323
|
+
["shortDescription", translation?.shortDescription ?? null],
|
|
324
|
+
["primaryMediaUrl", primaryMedia?.url ?? null],
|
|
325
|
+
["coverMediaUrl", primaryMedia?.url ?? null],
|
|
326
|
+
["durationDays", durationDays],
|
|
327
|
+
["latitude", coordinateLocation?.latitude ?? null],
|
|
328
|
+
["longitude", coordinateLocation?.longitude ?? null],
|
|
329
|
+
]);
|
|
330
|
+
if (translation?.name) {
|
|
331
|
+
out.set("name", translation.name);
|
|
332
|
+
}
|
|
333
|
+
return out;
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function pickTranslation(rows, locale) {
|
|
338
|
+
return (rows.find((row) => row.languageTag === locale) ??
|
|
339
|
+
rows.find((row) => row.languageTag.toLowerCase() === locale.toLowerCase()) ??
|
|
340
|
+
rows.find((row) => row.languageTag.split("-")[0] === locale.split("-")[0]) ??
|
|
341
|
+
rows[0] ??
|
|
342
|
+
null);
|
|
343
|
+
}
|
|
344
|
+
async function estimateItineraryDurationDays(db, itineraryId) {
|
|
345
|
+
const rows = await db
|
|
346
|
+
.select({ dayNumber: productDays.dayNumber })
|
|
347
|
+
.from(productDays)
|
|
348
|
+
.where(eq(productDays.itineraryId, itineraryId))
|
|
349
|
+
.orderBy(asc(productDays.dayNumber));
|
|
350
|
+
if (rows.length === 0)
|
|
351
|
+
return null;
|
|
352
|
+
const max = Math.max(...rows.map((row) => row.dayNumber));
|
|
353
|
+
return Number.isFinite(max) && max > 0 ? max : null;
|
|
354
|
+
}
|
|
355
|
+
function dateToEpochDays(value) {
|
|
356
|
+
if (!value)
|
|
357
|
+
return null;
|
|
358
|
+
const date = typeof value === "string" ? new Date(value) : value;
|
|
359
|
+
const time = date.getTime();
|
|
360
|
+
if (Number.isNaN(time))
|
|
361
|
+
return null;
|
|
362
|
+
return Math.floor(time / (24 * 60 * 60 * 1000));
|
|
363
|
+
}
|
package/dist/service.d.ts
CHANGED
|
@@ -574,13 +574,13 @@ export declare const productsService: {
|
|
|
574
574
|
updatedAt: Date;
|
|
575
575
|
productId: string;
|
|
576
576
|
title: string;
|
|
577
|
+
latitude: number | null;
|
|
578
|
+
longitude: number | null;
|
|
577
579
|
city: string | null;
|
|
578
580
|
address: string | null;
|
|
579
581
|
sortOrder: number;
|
|
580
582
|
countryCode: string | null;
|
|
581
583
|
locationType: "start" | "other" | "end" | "meeting_point" | "pickup" | "dropoff" | "point_of_interest";
|
|
582
|
-
latitude: number | null;
|
|
583
|
-
longitude: number | null;
|
|
584
584
|
googlePlaceId: string | null;
|
|
585
585
|
applePlaceId: string | null;
|
|
586
586
|
tripadvisorLocationId: string | null;
|
|
@@ -1013,8 +1013,8 @@ export declare const productsService: {
|
|
|
1013
1013
|
slug: string | null;
|
|
1014
1014
|
description: string | null;
|
|
1015
1015
|
productId: string;
|
|
1016
|
-
languageTag: string;
|
|
1017
1016
|
shortDescription: string | null;
|
|
1017
|
+
languageTag: string;
|
|
1018
1018
|
seoTitle: string | null;
|
|
1019
1019
|
seoDescription: string | null;
|
|
1020
1020
|
} | null>;
|
|
@@ -1066,8 +1066,8 @@ export declare const productsService: {
|
|
|
1066
1066
|
updatedAt: Date;
|
|
1067
1067
|
description: string | null;
|
|
1068
1068
|
optionId: string;
|
|
1069
|
-
languageTag: string;
|
|
1070
1069
|
shortDescription: string | null;
|
|
1070
|
+
languageTag: string;
|
|
1071
1071
|
} | null>;
|
|
1072
1072
|
updateOptionTranslation(db: PostgresJsDatabase, id: string, data: UpdateProductOptionTranslationInput): Promise<{
|
|
1073
1073
|
id: string;
|
|
@@ -1114,8 +1114,8 @@ export declare const productsService: {
|
|
|
1114
1114
|
updatedAt: Date;
|
|
1115
1115
|
description: string | null;
|
|
1116
1116
|
unitId: string;
|
|
1117
|
-
languageTag: string;
|
|
1118
1117
|
shortDescription: string | null;
|
|
1118
|
+
languageTag: string;
|
|
1119
1119
|
} | null>;
|
|
1120
1120
|
updateUnitTranslation(db: PostgresJsDatabase, id: string, data: UpdateOptionUnitTranslationInput): Promise<{
|
|
1121
1121
|
id: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voyantjs/products",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -125,12 +125,12 @@
|
|
|
125
125
|
"hono": "^4.12.10",
|
|
126
126
|
"pdf-lib": "^1.17.1",
|
|
127
127
|
"zod": "^4.3.6",
|
|
128
|
-
"@voyantjs/core": "0.
|
|
129
|
-
"@voyantjs/db": "0.
|
|
130
|
-
"@voyantjs/hono": "0.
|
|
131
|
-
"@voyantjs/utils": "0.
|
|
132
|
-
"@voyantjs/catalog": "0.
|
|
133
|
-
"@voyantjs/storage": "0.
|
|
128
|
+
"@voyantjs/core": "0.31.0",
|
|
129
|
+
"@voyantjs/db": "0.31.0",
|
|
130
|
+
"@voyantjs/hono": "0.31.0",
|
|
131
|
+
"@voyantjs/utils": "0.31.0",
|
|
132
|
+
"@voyantjs/catalog": "0.31.0",
|
|
133
|
+
"@voyantjs/storage": "0.31.0"
|
|
134
134
|
},
|
|
135
135
|
"devDependencies": {
|
|
136
136
|
"typescript": "^6.0.2",
|