@voyantjs/products 0.28.3 → 0.29.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/booking-engine/handler.d.ts +8 -0
- package/dist/booking-engine/handler.d.ts.map +1 -1
- package/dist/booking-engine/handler.js +38 -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 +41 -0
- package/dist/catalog-policy-destinations.d.ts.map +1 -0
- package/dist/catalog-policy-destinations.js +121 -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/events.d.ts +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/routes.d.ts +254 -19
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +73 -5
- package/dist/schema-taxonomy.d.ts +287 -0
- package/dist/schema-taxonomy.d.ts.map +1 -1
- package/dist/schema-taxonomy.js +47 -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 +119 -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 +46 -1
- package/dist/service-catalog-plane.d.ts.map +1 -1
- package/dist/service-catalog-plane.js +42 -6
- package/dist/service.d.ts +98 -19
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +142 -1
- package/dist/tasks/brochures.d.ts +1 -1
- package/dist/validation-content.d.ts +38 -0
- package/dist/validation-content.d.ts.map +1 -1
- package/dist/validation-content.js +29 -0
- package/package.json +77 -7
|
@@ -360,6 +360,178 @@ export declare const productCategories: import("drizzle-orm/pg-core").PgTableWit
|
|
|
360
360
|
}>;
|
|
361
361
|
export type ProductCategory = typeof productCategories.$inferSelect;
|
|
362
362
|
export type NewProductCategory = typeof productCategories.$inferInsert;
|
|
363
|
+
/**
|
|
364
|
+
* Locale-aware category labels. Mirrors `destinationTranslations`.
|
|
365
|
+
*
|
|
366
|
+
* The catalog plane's taxonomy projection (`catalog-policy-taxonomy.ts` +
|
|
367
|
+
* `service-catalog-plane-taxonomy.ts`) reads this table per-slice locale
|
|
368
|
+
* and falls back to `productCategories.name` when no row exists for a
|
|
369
|
+
* given `(categoryId, languageTag)`. Slug stays single-locale on
|
|
370
|
+
* `productCategories.slug` per #502 non-goals — operators want stable
|
|
371
|
+
* URLs that don't shift when translations are edited.
|
|
372
|
+
*/
|
|
373
|
+
export declare const productCategoryTranslations: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
374
|
+
name: "product_category_translations";
|
|
375
|
+
schema: undefined;
|
|
376
|
+
columns: {
|
|
377
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
378
|
+
name: string;
|
|
379
|
+
tableName: "product_category_translations";
|
|
380
|
+
dataType: "string";
|
|
381
|
+
columnType: "PgText";
|
|
382
|
+
data: string;
|
|
383
|
+
driverParam: string;
|
|
384
|
+
notNull: true;
|
|
385
|
+
hasDefault: true;
|
|
386
|
+
isPrimaryKey: true;
|
|
387
|
+
isAutoincrement: false;
|
|
388
|
+
hasRuntimeDefault: true;
|
|
389
|
+
enumValues: [string, ...string[]];
|
|
390
|
+
baseColumn: never;
|
|
391
|
+
identity: undefined;
|
|
392
|
+
generated: undefined;
|
|
393
|
+
}, {}, {}>;
|
|
394
|
+
categoryId: import("drizzle-orm/pg-core").PgColumn<{
|
|
395
|
+
name: string;
|
|
396
|
+
tableName: "product_category_translations";
|
|
397
|
+
dataType: "string";
|
|
398
|
+
columnType: "PgText";
|
|
399
|
+
data: string;
|
|
400
|
+
driverParam: string;
|
|
401
|
+
notNull: true;
|
|
402
|
+
hasDefault: false;
|
|
403
|
+
isPrimaryKey: false;
|
|
404
|
+
isAutoincrement: false;
|
|
405
|
+
hasRuntimeDefault: false;
|
|
406
|
+
enumValues: [string, ...string[]];
|
|
407
|
+
baseColumn: never;
|
|
408
|
+
identity: undefined;
|
|
409
|
+
generated: undefined;
|
|
410
|
+
}, {}, {}>;
|
|
411
|
+
languageTag: import("drizzle-orm/pg-core").PgColumn<{
|
|
412
|
+
name: "language_tag";
|
|
413
|
+
tableName: "product_category_translations";
|
|
414
|
+
dataType: "string";
|
|
415
|
+
columnType: "PgText";
|
|
416
|
+
data: string;
|
|
417
|
+
driverParam: string;
|
|
418
|
+
notNull: true;
|
|
419
|
+
hasDefault: false;
|
|
420
|
+
isPrimaryKey: false;
|
|
421
|
+
isAutoincrement: false;
|
|
422
|
+
hasRuntimeDefault: false;
|
|
423
|
+
enumValues: [string, ...string[]];
|
|
424
|
+
baseColumn: never;
|
|
425
|
+
identity: undefined;
|
|
426
|
+
generated: undefined;
|
|
427
|
+
}, {}, {}>;
|
|
428
|
+
name: import("drizzle-orm/pg-core").PgColumn<{
|
|
429
|
+
name: "name";
|
|
430
|
+
tableName: "product_category_translations";
|
|
431
|
+
dataType: "string";
|
|
432
|
+
columnType: "PgText";
|
|
433
|
+
data: string;
|
|
434
|
+
driverParam: string;
|
|
435
|
+
notNull: true;
|
|
436
|
+
hasDefault: false;
|
|
437
|
+
isPrimaryKey: false;
|
|
438
|
+
isAutoincrement: false;
|
|
439
|
+
hasRuntimeDefault: false;
|
|
440
|
+
enumValues: [string, ...string[]];
|
|
441
|
+
baseColumn: never;
|
|
442
|
+
identity: undefined;
|
|
443
|
+
generated: undefined;
|
|
444
|
+
}, {}, {}>;
|
|
445
|
+
description: import("drizzle-orm/pg-core").PgColumn<{
|
|
446
|
+
name: "description";
|
|
447
|
+
tableName: "product_category_translations";
|
|
448
|
+
dataType: "string";
|
|
449
|
+
columnType: "PgText";
|
|
450
|
+
data: string;
|
|
451
|
+
driverParam: string;
|
|
452
|
+
notNull: false;
|
|
453
|
+
hasDefault: false;
|
|
454
|
+
isPrimaryKey: false;
|
|
455
|
+
isAutoincrement: false;
|
|
456
|
+
hasRuntimeDefault: false;
|
|
457
|
+
enumValues: [string, ...string[]];
|
|
458
|
+
baseColumn: never;
|
|
459
|
+
identity: undefined;
|
|
460
|
+
generated: undefined;
|
|
461
|
+
}, {}, {}>;
|
|
462
|
+
seoTitle: import("drizzle-orm/pg-core").PgColumn<{
|
|
463
|
+
name: "seo_title";
|
|
464
|
+
tableName: "product_category_translations";
|
|
465
|
+
dataType: "string";
|
|
466
|
+
columnType: "PgText";
|
|
467
|
+
data: string;
|
|
468
|
+
driverParam: string;
|
|
469
|
+
notNull: false;
|
|
470
|
+
hasDefault: false;
|
|
471
|
+
isPrimaryKey: false;
|
|
472
|
+
isAutoincrement: false;
|
|
473
|
+
hasRuntimeDefault: false;
|
|
474
|
+
enumValues: [string, ...string[]];
|
|
475
|
+
baseColumn: never;
|
|
476
|
+
identity: undefined;
|
|
477
|
+
generated: undefined;
|
|
478
|
+
}, {}, {}>;
|
|
479
|
+
seoDescription: import("drizzle-orm/pg-core").PgColumn<{
|
|
480
|
+
name: "seo_description";
|
|
481
|
+
tableName: "product_category_translations";
|
|
482
|
+
dataType: "string";
|
|
483
|
+
columnType: "PgText";
|
|
484
|
+
data: string;
|
|
485
|
+
driverParam: string;
|
|
486
|
+
notNull: false;
|
|
487
|
+
hasDefault: false;
|
|
488
|
+
isPrimaryKey: false;
|
|
489
|
+
isAutoincrement: false;
|
|
490
|
+
hasRuntimeDefault: false;
|
|
491
|
+
enumValues: [string, ...string[]];
|
|
492
|
+
baseColumn: never;
|
|
493
|
+
identity: undefined;
|
|
494
|
+
generated: undefined;
|
|
495
|
+
}, {}, {}>;
|
|
496
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
497
|
+
name: "created_at";
|
|
498
|
+
tableName: "product_category_translations";
|
|
499
|
+
dataType: "date";
|
|
500
|
+
columnType: "PgTimestamp";
|
|
501
|
+
data: Date;
|
|
502
|
+
driverParam: string;
|
|
503
|
+
notNull: true;
|
|
504
|
+
hasDefault: true;
|
|
505
|
+
isPrimaryKey: false;
|
|
506
|
+
isAutoincrement: false;
|
|
507
|
+
hasRuntimeDefault: false;
|
|
508
|
+
enumValues: undefined;
|
|
509
|
+
baseColumn: never;
|
|
510
|
+
identity: undefined;
|
|
511
|
+
generated: undefined;
|
|
512
|
+
}, {}, {}>;
|
|
513
|
+
updatedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
514
|
+
name: "updated_at";
|
|
515
|
+
tableName: "product_category_translations";
|
|
516
|
+
dataType: "date";
|
|
517
|
+
columnType: "PgTimestamp";
|
|
518
|
+
data: Date;
|
|
519
|
+
driverParam: string;
|
|
520
|
+
notNull: true;
|
|
521
|
+
hasDefault: true;
|
|
522
|
+
isPrimaryKey: false;
|
|
523
|
+
isAutoincrement: false;
|
|
524
|
+
hasRuntimeDefault: false;
|
|
525
|
+
enumValues: undefined;
|
|
526
|
+
baseColumn: never;
|
|
527
|
+
identity: undefined;
|
|
528
|
+
generated: undefined;
|
|
529
|
+
}, {}, {}>;
|
|
530
|
+
};
|
|
531
|
+
dialect: "pg";
|
|
532
|
+
}>;
|
|
533
|
+
export type ProductCategoryTranslation = typeof productCategoryTranslations.$inferSelect;
|
|
534
|
+
export type NewProductCategoryTranslation = typeof productCategoryTranslations.$inferInsert;
|
|
363
535
|
export declare const productTags: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
364
536
|
name: "product_tags";
|
|
365
537
|
schema: undefined;
|
|
@@ -437,6 +609,121 @@ export declare const productTags: import("drizzle-orm/pg-core").PgTableWithColum
|
|
|
437
609
|
}>;
|
|
438
610
|
export type ProductTag = typeof productTags.$inferSelect;
|
|
439
611
|
export type NewProductTag = typeof productTags.$inferInsert;
|
|
612
|
+
/**
|
|
613
|
+
* Locale-aware tag labels. Slimmer than category translations — tags are
|
|
614
|
+
* short labels with no description / SEO blurbs (per #502 non-goals).
|
|
615
|
+
*/
|
|
616
|
+
export declare const productTagTranslations: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
617
|
+
name: "product_tag_translations";
|
|
618
|
+
schema: undefined;
|
|
619
|
+
columns: {
|
|
620
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
621
|
+
name: string;
|
|
622
|
+
tableName: "product_tag_translations";
|
|
623
|
+
dataType: "string";
|
|
624
|
+
columnType: "PgText";
|
|
625
|
+
data: string;
|
|
626
|
+
driverParam: string;
|
|
627
|
+
notNull: true;
|
|
628
|
+
hasDefault: true;
|
|
629
|
+
isPrimaryKey: true;
|
|
630
|
+
isAutoincrement: false;
|
|
631
|
+
hasRuntimeDefault: true;
|
|
632
|
+
enumValues: [string, ...string[]];
|
|
633
|
+
baseColumn: never;
|
|
634
|
+
identity: undefined;
|
|
635
|
+
generated: undefined;
|
|
636
|
+
}, {}, {}>;
|
|
637
|
+
tagId: import("drizzle-orm/pg-core").PgColumn<{
|
|
638
|
+
name: string;
|
|
639
|
+
tableName: "product_tag_translations";
|
|
640
|
+
dataType: "string";
|
|
641
|
+
columnType: "PgText";
|
|
642
|
+
data: string;
|
|
643
|
+
driverParam: string;
|
|
644
|
+
notNull: true;
|
|
645
|
+
hasDefault: false;
|
|
646
|
+
isPrimaryKey: false;
|
|
647
|
+
isAutoincrement: false;
|
|
648
|
+
hasRuntimeDefault: false;
|
|
649
|
+
enumValues: [string, ...string[]];
|
|
650
|
+
baseColumn: never;
|
|
651
|
+
identity: undefined;
|
|
652
|
+
generated: undefined;
|
|
653
|
+
}, {}, {}>;
|
|
654
|
+
languageTag: import("drizzle-orm/pg-core").PgColumn<{
|
|
655
|
+
name: "language_tag";
|
|
656
|
+
tableName: "product_tag_translations";
|
|
657
|
+
dataType: "string";
|
|
658
|
+
columnType: "PgText";
|
|
659
|
+
data: string;
|
|
660
|
+
driverParam: string;
|
|
661
|
+
notNull: true;
|
|
662
|
+
hasDefault: false;
|
|
663
|
+
isPrimaryKey: false;
|
|
664
|
+
isAutoincrement: false;
|
|
665
|
+
hasRuntimeDefault: false;
|
|
666
|
+
enumValues: [string, ...string[]];
|
|
667
|
+
baseColumn: never;
|
|
668
|
+
identity: undefined;
|
|
669
|
+
generated: undefined;
|
|
670
|
+
}, {}, {}>;
|
|
671
|
+
name: import("drizzle-orm/pg-core").PgColumn<{
|
|
672
|
+
name: "name";
|
|
673
|
+
tableName: "product_tag_translations";
|
|
674
|
+
dataType: "string";
|
|
675
|
+
columnType: "PgText";
|
|
676
|
+
data: string;
|
|
677
|
+
driverParam: string;
|
|
678
|
+
notNull: true;
|
|
679
|
+
hasDefault: false;
|
|
680
|
+
isPrimaryKey: false;
|
|
681
|
+
isAutoincrement: false;
|
|
682
|
+
hasRuntimeDefault: false;
|
|
683
|
+
enumValues: [string, ...string[]];
|
|
684
|
+
baseColumn: never;
|
|
685
|
+
identity: undefined;
|
|
686
|
+
generated: undefined;
|
|
687
|
+
}, {}, {}>;
|
|
688
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
689
|
+
name: "created_at";
|
|
690
|
+
tableName: "product_tag_translations";
|
|
691
|
+
dataType: "date";
|
|
692
|
+
columnType: "PgTimestamp";
|
|
693
|
+
data: Date;
|
|
694
|
+
driverParam: string;
|
|
695
|
+
notNull: true;
|
|
696
|
+
hasDefault: true;
|
|
697
|
+
isPrimaryKey: false;
|
|
698
|
+
isAutoincrement: false;
|
|
699
|
+
hasRuntimeDefault: false;
|
|
700
|
+
enumValues: undefined;
|
|
701
|
+
baseColumn: never;
|
|
702
|
+
identity: undefined;
|
|
703
|
+
generated: undefined;
|
|
704
|
+
}, {}, {}>;
|
|
705
|
+
updatedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
706
|
+
name: "updated_at";
|
|
707
|
+
tableName: "product_tag_translations";
|
|
708
|
+
dataType: "date";
|
|
709
|
+
columnType: "PgTimestamp";
|
|
710
|
+
data: Date;
|
|
711
|
+
driverParam: string;
|
|
712
|
+
notNull: true;
|
|
713
|
+
hasDefault: true;
|
|
714
|
+
isPrimaryKey: false;
|
|
715
|
+
isAutoincrement: false;
|
|
716
|
+
hasRuntimeDefault: false;
|
|
717
|
+
enumValues: undefined;
|
|
718
|
+
baseColumn: never;
|
|
719
|
+
identity: undefined;
|
|
720
|
+
generated: undefined;
|
|
721
|
+
}, {}, {}>;
|
|
722
|
+
};
|
|
723
|
+
dialect: "pg";
|
|
724
|
+
}>;
|
|
725
|
+
export type ProductTagTranslation = typeof productTagTranslations.$inferSelect;
|
|
726
|
+
export type NewProductTagTranslation = typeof productTagTranslations.$inferInsert;
|
|
440
727
|
export declare const destinations: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
441
728
|
name: "destinations";
|
|
442
729
|
schema: undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-taxonomy.d.ts","sourceRoot":"","sources":["../src/schema-taxonomy.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmBxB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,YAAY,CAAC,YAAY,CAAA;AAC1D,MAAM,MAAM,cAAc,GAAG,OAAO,YAAY,CAAC,YAAY,CAAA;AAE7D,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuC7B,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,OAAO,iBAAiB,CAAC,YAAY,CAAA;AACnE,MAAM,MAAM,kBAAkB,GAAG,OAAO,iBAAiB,CAAC,YAAY,CAAA;AAEtE,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASvB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,OAAO,WAAW,CAAC,YAAY,CAAA;AACxD,MAAM,MAAM,aAAa,GAAG,OAAO,WAAW,CAAC,YAAY,CAAA;AAE3D,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwBxB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,YAAY,CAAC,YAAY,CAAA;AAC1D,MAAM,MAAM,cAAc,GAAG,OAAO,YAAY,CAAC,YAAY,CAAA;AAE7D,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyBnC,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG,OAAO,uBAAuB,CAAC,YAAY,CAAA;AAChF,MAAM,MAAM,yBAAyB,GAAG,OAAO,uBAAuB,CAAC,YAAY,CAAA;AAEnF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkBnC,CAAA;AAED,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAe9B,CAAA;AAED,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkB/B,CAAA"}
|
|
1
|
+
{"version":3,"file":"schema-taxonomy.d.ts","sourceRoot":"","sources":["../src/schema-taxonomy.ts"],"names":[],"mappings":"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"}
|
package/dist/schema-taxonomy.js
CHANGED
|
@@ -49,12 +49,59 @@ export const productCategories = pgTable("product_categories", {
|
|
|
49
49
|
index("idx_product_categories_active_sort_name").on(table.active, table.sortOrder, table.name),
|
|
50
50
|
index("idx_product_categories_parent_sort_name").on(table.parentId, table.sortOrder, table.name),
|
|
51
51
|
]);
|
|
52
|
+
/**
|
|
53
|
+
* Locale-aware category labels. Mirrors `destinationTranslations`.
|
|
54
|
+
*
|
|
55
|
+
* The catalog plane's taxonomy projection (`catalog-policy-taxonomy.ts` +
|
|
56
|
+
* `service-catalog-plane-taxonomy.ts`) reads this table per-slice locale
|
|
57
|
+
* and falls back to `productCategories.name` when no row exists for a
|
|
58
|
+
* given `(categoryId, languageTag)`. Slug stays single-locale on
|
|
59
|
+
* `productCategories.slug` per #502 non-goals — operators want stable
|
|
60
|
+
* URLs that don't shift when translations are edited.
|
|
61
|
+
*/
|
|
62
|
+
export const productCategoryTranslations = pgTable("product_category_translations", {
|
|
63
|
+
id: typeId("product_category_translations"),
|
|
64
|
+
categoryId: typeIdRef("category_id")
|
|
65
|
+
.notNull()
|
|
66
|
+
.references(() => productCategories.id, { onDelete: "cascade" }),
|
|
67
|
+
languageTag: text("language_tag").notNull(),
|
|
68
|
+
name: text("name").notNull(),
|
|
69
|
+
description: text("description"),
|
|
70
|
+
seoTitle: text("seo_title"),
|
|
71
|
+
seoDescription: text("seo_description"),
|
|
72
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
73
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
74
|
+
}, (table) => [
|
|
75
|
+
uniqueIndex("uidx_product_category_translations_locale").on(table.categoryId, table.languageTag),
|
|
76
|
+
index("idx_product_category_translations_language").on(table.languageTag),
|
|
77
|
+
index("idx_product_category_translations_category_language_created").on(table.categoryId, table.languageTag, table.createdAt),
|
|
78
|
+
index("idx_product_category_translations_language_created").on(table.languageTag, table.createdAt),
|
|
79
|
+
]);
|
|
52
80
|
export const productTags = pgTable("product_tags", {
|
|
53
81
|
id: typeId("product_tags"),
|
|
54
82
|
name: text("name").notNull(),
|
|
55
83
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
56
84
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
57
85
|
}, (table) => [uniqueIndex("uidx_product_tags_name").on(table.name)]);
|
|
86
|
+
/**
|
|
87
|
+
* Locale-aware tag labels. Slimmer than category translations — tags are
|
|
88
|
+
* short labels with no description / SEO blurbs (per #502 non-goals).
|
|
89
|
+
*/
|
|
90
|
+
export const productTagTranslations = pgTable("product_tag_translations", {
|
|
91
|
+
id: typeId("product_tag_translations"),
|
|
92
|
+
tagId: typeIdRef("tag_id")
|
|
93
|
+
.notNull()
|
|
94
|
+
.references(() => productTags.id, { onDelete: "cascade" }),
|
|
95
|
+
languageTag: text("language_tag").notNull(),
|
|
96
|
+
name: text("name").notNull(),
|
|
97
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
98
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
99
|
+
}, (table) => [
|
|
100
|
+
uniqueIndex("uidx_product_tag_translations_locale").on(table.tagId, table.languageTag),
|
|
101
|
+
index("idx_product_tag_translations_language").on(table.languageTag),
|
|
102
|
+
index("idx_product_tag_translations_tag_language_created").on(table.tagId, table.languageTag, table.createdAt),
|
|
103
|
+
index("idx_product_tag_translations_language_created").on(table.languageTag, table.createdAt),
|
|
104
|
+
]);
|
|
58
105
|
export const destinations = pgTable("destinations", {
|
|
59
106
|
id: typeId("destinations"),
|
|
60
107
|
parentId: typeIdRef("parent_id"),
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection extension that joins product → destinations and contributes
|
|
3
|
+
* locale-aware destination fields (regions, countries, cities, slugs, ids)
|
|
4
|
+
* onto the product search document.
|
|
5
|
+
*
|
|
6
|
+
* Wire via `createProductDocumentBuilder({ extensions: [destinationsExtension] })`.
|
|
7
|
+
* Requires the registry to include `productDestinationsCatalogPolicy` —
|
|
8
|
+
* otherwise the contributed fields are silently dropped by the indexer's
|
|
9
|
+
* field-policy filter.
|
|
10
|
+
*
|
|
11
|
+
* Locale handling: `destination_translations` is keyed by `(destination_id,
|
|
12
|
+
* language_tag)`. The projection looks up the slice's locale; missing
|
|
13
|
+
* translations fall back to the destination's canonical `slug`. Operators
|
|
14
|
+
* who want the locale lookup to fall back to a different language (e.g.
|
|
15
|
+
* `fr-CA` → `fr` → `en`) should pre-populate the translation row;
|
|
16
|
+
* multi-tier fallback isn't built into the joins.
|
|
17
|
+
*
|
|
18
|
+
* Today's destinations table has no coordinate columns — geopoints come
|
|
19
|
+
* from `product_locations` and will ship as a separate projection
|
|
20
|
+
* extension once that policy lands.
|
|
21
|
+
*/
|
|
22
|
+
import type { ProductProjectionExtension } from "./service-catalog-plane.js";
|
|
23
|
+
/**
|
|
24
|
+
* Construct the destinations projection extension.
|
|
25
|
+
*
|
|
26
|
+
* Returns a `ProductProjectionExtension` ready to pass to
|
|
27
|
+
* `createProductDocumentBuilder`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createProductDestinationsProjectionExtension(): ProductProjectionExtension;
|
|
30
|
+
//# sourceMappingURL=service-catalog-plane-destinations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-catalog-plane-destinations.d.ts","sourceRoot":"","sources":["../src/service-catalog-plane-destinations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAA;AAgG5E;;;;;GAKG;AACH,wBAAgB,4CAA4C,IAAI,0BAA0B,CAyBzF"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection extension that joins product → destinations and contributes
|
|
3
|
+
* locale-aware destination fields (regions, countries, cities, slugs, ids)
|
|
4
|
+
* onto the product search document.
|
|
5
|
+
*
|
|
6
|
+
* Wire via `createProductDocumentBuilder({ extensions: [destinationsExtension] })`.
|
|
7
|
+
* Requires the registry to include `productDestinationsCatalogPolicy` —
|
|
8
|
+
* otherwise the contributed fields are silently dropped by the indexer's
|
|
9
|
+
* field-policy filter.
|
|
10
|
+
*
|
|
11
|
+
* Locale handling: `destination_translations` is keyed by `(destination_id,
|
|
12
|
+
* language_tag)`. The projection looks up the slice's locale; missing
|
|
13
|
+
* translations fall back to the destination's canonical `slug`. Operators
|
|
14
|
+
* who want the locale lookup to fall back to a different language (e.g.
|
|
15
|
+
* `fr-CA` → `fr` → `en`) should pre-populate the translation row;
|
|
16
|
+
* multi-tier fallback isn't built into the joins.
|
|
17
|
+
*
|
|
18
|
+
* Today's destinations table has no coordinate columns — geopoints come
|
|
19
|
+
* from `product_locations` and will ship as a separate projection
|
|
20
|
+
* extension once that policy lands.
|
|
21
|
+
*/
|
|
22
|
+
import { and, eq, inArray } from "drizzle-orm";
|
|
23
|
+
import { destinations, destinationTranslations, productDestinations } from "./schema-taxonomy.js";
|
|
24
|
+
async function joinProductDestinations(db, productId, languageTag) {
|
|
25
|
+
// Sub-query the linked destination ids first so the locale lookup is
|
|
26
|
+
// single-shot. Drizzle's join builder doesn't compose left joins on
|
|
27
|
+
// composite predicates cleanly across all dialects, so two queries +
|
|
28
|
+
// an in-memory merge keeps the contract obvious.
|
|
29
|
+
const links = await db
|
|
30
|
+
.select({
|
|
31
|
+
destinationId: productDestinations.destinationId,
|
|
32
|
+
destinationType: destinations.destinationType,
|
|
33
|
+
slug: destinations.slug,
|
|
34
|
+
active: destinations.active,
|
|
35
|
+
})
|
|
36
|
+
.from(productDestinations)
|
|
37
|
+
.innerJoin(destinations, eq(productDestinations.destinationId, destinations.id))
|
|
38
|
+
.where(eq(productDestinations.productId, productId));
|
|
39
|
+
const activeLinks = links.filter((row) => row.active);
|
|
40
|
+
if (activeLinks.length === 0)
|
|
41
|
+
return [];
|
|
42
|
+
const destinationIds = activeLinks.map((row) => row.destinationId);
|
|
43
|
+
const translations = await db
|
|
44
|
+
.select({
|
|
45
|
+
destinationId: destinationTranslations.destinationId,
|
|
46
|
+
name: destinationTranslations.name,
|
|
47
|
+
})
|
|
48
|
+
.from(destinationTranslations)
|
|
49
|
+
.where(and(inArray(destinationTranslations.destinationId, destinationIds), eq(destinationTranslations.languageTag, languageTag)));
|
|
50
|
+
const nameByDestinationId = new Map();
|
|
51
|
+
for (const row of translations) {
|
|
52
|
+
nameByDestinationId.set(row.destinationId, row.name);
|
|
53
|
+
}
|
|
54
|
+
return activeLinks.map((row) => ({
|
|
55
|
+
destinationId: row.destinationId,
|
|
56
|
+
destinationType: row.destinationType,
|
|
57
|
+
slug: row.slug,
|
|
58
|
+
translatedName: nameByDestinationId.get(row.destinationId) ?? null,
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
/** Bucket destinations by `destinationType` and return locale-aware names. */
|
|
62
|
+
function bucketByType(rows) {
|
|
63
|
+
const regions = [];
|
|
64
|
+
const countries = [];
|
|
65
|
+
const cities = [];
|
|
66
|
+
const slugs = [];
|
|
67
|
+
const ids = [];
|
|
68
|
+
for (const row of rows) {
|
|
69
|
+
const label = row.translatedName ?? row.slug;
|
|
70
|
+
switch (row.destinationType) {
|
|
71
|
+
case "region":
|
|
72
|
+
regions.push(label);
|
|
73
|
+
break;
|
|
74
|
+
case "country":
|
|
75
|
+
countries.push(label);
|
|
76
|
+
break;
|
|
77
|
+
case "city":
|
|
78
|
+
cities.push(label);
|
|
79
|
+
break;
|
|
80
|
+
// "destination" (the catch-all default) doesn't bucket into any of
|
|
81
|
+
// the typed lists. Its slug + id still land in the doc so storefronts
|
|
82
|
+
// can filter generically.
|
|
83
|
+
}
|
|
84
|
+
slugs.push(row.slug);
|
|
85
|
+
ids.push(row.destinationId);
|
|
86
|
+
}
|
|
87
|
+
return { regions, countries, cities, slugs, ids };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Construct the destinations projection extension.
|
|
91
|
+
*
|
|
92
|
+
* Returns a `ProductProjectionExtension` ready to pass to
|
|
93
|
+
* `createProductDocumentBuilder`.
|
|
94
|
+
*/
|
|
95
|
+
export function createProductDestinationsProjectionExtension() {
|
|
96
|
+
return {
|
|
97
|
+
name: "products:destinations",
|
|
98
|
+
async project(db, productId, slice) {
|
|
99
|
+
const rows = await joinProductDestinations(db, productId, slice.locale);
|
|
100
|
+
if (rows.length === 0) {
|
|
101
|
+
return new Map([
|
|
102
|
+
["regions[]", []],
|
|
103
|
+
["countries[]", []],
|
|
104
|
+
["cities[]", []],
|
|
105
|
+
["destinationSlugs[]", []],
|
|
106
|
+
["destinationIds[]", []],
|
|
107
|
+
]);
|
|
108
|
+
}
|
|
109
|
+
const { regions, countries, cities, slugs, ids } = bucketByType(rows);
|
|
110
|
+
return new Map([
|
|
111
|
+
["regions[]", regions],
|
|
112
|
+
["countries[]", countries],
|
|
113
|
+
["cities[]", cities],
|
|
114
|
+
["destinationSlugs[]", slugs],
|
|
115
|
+
["destinationIds[]", ids],
|
|
116
|
+
]);
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection extension that joins product → categories (with ancestor walk)
|
|
3
|
+
* and product → tags, contributing taxonomy fields onto the product search
|
|
4
|
+
* document.
|
|
5
|
+
*
|
|
6
|
+
* Wire via `createProductDocumentBuilder({ extensions: [taxonomyExtension] })`.
|
|
7
|
+
* Requires the registry to include `productTaxonomyCatalogPolicy` —
|
|
8
|
+
* otherwise the contributed fields are silently dropped by the indexer's
|
|
9
|
+
* field-policy filter.
|
|
10
|
+
*
|
|
11
|
+
* Hierarchy denormalization: Typesense can't recurse, so a product linked
|
|
12
|
+
* to "Hiking" (parent: "Adventure") needs both labels in `categories[]` for
|
|
13
|
+
* the "Adventure" filter to match. The projection walks the parent chain
|
|
14
|
+
* iteratively, filtering inactive ancestors (so an operator-paused parent
|
|
15
|
+
* stops surfacing its still-active children under the parent filter).
|
|
16
|
+
*
|
|
17
|
+
* Localization (#502): when a `product_category_translations` /
|
|
18
|
+
* `product_tag_translations` row exists for the slice's locale, its `name`
|
|
19
|
+
* wins. Otherwise the projection falls back to the canonical
|
|
20
|
+
* `productCategories.name` / `productTags.name` (the legacy single-locale
|
|
21
|
+
* column). This makes the upgrade non-breaking — operators that haven't
|
|
22
|
+
* created translations keep seeing the English label on every locale slice
|
|
23
|
+
* exactly as before.
|
|
24
|
+
*
|
|
25
|
+
* Slugs stay single-locale on `productCategories.slug` (per #502 non-goals
|
|
26
|
+
* — operators want stable URLs that don't shift when translations are
|
|
27
|
+
* edited).
|
|
28
|
+
*/
|
|
29
|
+
import type { ProductProjectionExtension } from "./service-catalog-plane.js";
|
|
30
|
+
interface CategoryRow {
|
|
31
|
+
id: string;
|
|
32
|
+
parentId: string | null;
|
|
33
|
+
name: string;
|
|
34
|
+
slug: string;
|
|
35
|
+
active: boolean;
|
|
36
|
+
}
|
|
37
|
+
interface DirectCategoryLink {
|
|
38
|
+
categoryId: string;
|
|
39
|
+
sortOrder: number;
|
|
40
|
+
}
|
|
41
|
+
interface TagRow {
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
}
|
|
45
|
+
interface TaxonomyProjection {
|
|
46
|
+
categoryIds: string[];
|
|
47
|
+
categoryNames: string[];
|
|
48
|
+
categorySlugs: string[];
|
|
49
|
+
primaryCategoryId: string | null;
|
|
50
|
+
primaryCategoryName: string | null;
|
|
51
|
+
primaryCategorySlug: string | null;
|
|
52
|
+
tagIds: string[];
|
|
53
|
+
tagLabels: string[];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Pure aggregation kernel. `categoryNameByLocale` and `tagNameByLocale`
|
|
57
|
+
* carry slice-locale translations; entries override the canonical name on
|
|
58
|
+
* the row. Missing entries fall back to the canonical name. Slug stays
|
|
59
|
+
* canonical regardless — there's no per-locale slug.
|
|
60
|
+
*/
|
|
61
|
+
declare function buildTaxonomyProjection(directLinks: ReadonlyArray<DirectCategoryLink>, resolvedCategories: ReadonlyMap<string, CategoryRow>, tags: ReadonlyArray<TagRow>, categoryNameByLocale?: ReadonlyMap<string, string>, tagNameByLocale?: ReadonlyMap<string, string>): TaxonomyProjection;
|
|
62
|
+
/**
|
|
63
|
+
* Construct the taxonomy projection extension.
|
|
64
|
+
*
|
|
65
|
+
* Returns a `ProductProjectionExtension` ready to pass to
|
|
66
|
+
* `createProductDocumentBuilder`.
|
|
67
|
+
*/
|
|
68
|
+
export declare function createProductTaxonomyProjectionExtension(): ProductProjectionExtension;
|
|
69
|
+
export declare const __test__: {
|
|
70
|
+
buildTaxonomyProjection: typeof buildTaxonomyProjection;
|
|
71
|
+
};
|
|
72
|
+
export {};
|
|
73
|
+
//# sourceMappingURL=service-catalog-plane-taxonomy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-catalog-plane-taxonomy.d.ts","sourceRoot":"","sources":["../src/service-catalog-plane-taxonomy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAaH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAA;AAE5E,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,UAAU,kBAAkB;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CAClB;AAkED,UAAU,MAAM;IACd,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;CACb;AA8DD,UAAU,kBAAkB;IAC1B,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,SAAS,EAAE,MAAM,EAAE,CAAA;CACpB;AAED;;;;;GAKG;AACH,iBAAS,uBAAuB,CAC9B,WAAW,EAAE,aAAa,CAAC,kBAAkB,CAAC,EAC9C,kBAAkB,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,EACpD,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,oBAAoB,GAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAa,EAC7D,eAAe,GAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAa,GACvD,kBAAkB,CAmEpB;AAED;;;;;GAKG;AACH,wBAAgB,wCAAwC,IAAI,0BAA0B,CA6CrF;AAGD,eAAO,MAAM,QAAQ;;CAEpB,CAAA"}
|