@cavuno/board 3.1.0 → 3.2.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/blog-DGBHmExV.d.mts +29 -0
  3. package/dist/blog-iV9XEIGf.d.ts +29 -0
  4. package/dist/{salaries-ChSqDBAM.d.mts → board-BJbXd_9_.d.mts} +14 -27
  5. package/dist/{salaries-BahBpxWT.d.ts → board-JhdUWIoq.d.ts} +14 -27
  6. package/dist/breadcrumbs-1hMjcsqA.d.mts +171 -0
  7. package/dist/breadcrumbs-DexU07TT.d.ts +171 -0
  8. package/dist/chunk-2ETP5UH4.mjs +271 -0
  9. package/dist/chunk-DLSO6XXY.mjs +33 -0
  10. package/dist/chunk-IJCJ26SQ.mjs +596 -0
  11. package/dist/chunk-L7ZOYWHW.mjs +98 -0
  12. package/dist/chunk-LUGJWOT7.mjs +96 -0
  13. package/dist/chunk-NXY3OWGF.mjs +1060 -0
  14. package/dist/chunk-STE77MAT.mjs +844 -0
  15. package/dist/filters.mjs +7 -668
  16. package/dist/format.d.mts +2 -1
  17. package/dist/format.d.ts +2 -1
  18. package/dist/format.js +592 -523
  19. package/dist/format.mjs +19 -680
  20. package/dist/go.mjs +4 -29
  21. package/dist/index.d.mts +5 -4
  22. package/dist/index.d.ts +5 -4
  23. package/dist/index.js +1 -1
  24. package/dist/index.mjs +22 -278
  25. package/dist/paths.mjs +20 -77
  26. package/dist/route-contract.mjs +30 -1079
  27. package/dist/seo.d.mts +14 -171
  28. package/dist/seo.d.ts +14 -171
  29. package/dist/seo.js +1 -1
  30. package/dist/seo.mjs +24 -1399
  31. package/dist/server.d.mts +2 -2
  32. package/dist/server.d.ts +2 -2
  33. package/dist/server.mjs +5 -31
  34. package/dist/sitemap.d.mts +2 -2
  35. package/dist/sitemap.d.ts +2 -2
  36. package/dist/sitemap.mjs +20 -96
  37. package/dist/well-known.mjs +4 -421
  38. package/package.json +6 -3
  39. package/skills/manifest.json +1 -1
  40. package/dist/board-NE98PlwN.d.ts +0 -16
  41. package/dist/board-muu8BI8P.d.mts +0 -16
package/dist/format.js CHANGED
@@ -22,6 +22,7 @@ var format_exports = {};
22
22
  __export(format_exports, {
23
23
  COUNTRY_CODES: () => COUNTRY_CODES,
24
24
  PUBLIC_LABEL_GROUPS: () => PUBLIC_LABEL_GROUPS,
25
+ buildJobBreadcrumbs: () => buildJobBreadcrumbs,
25
26
  cardLocationLabel: () => cardLocationLabel,
26
27
  companyIntro: () => companyIntro,
27
28
  countryOptions: () => countryOptions,
@@ -30,9 +31,12 @@ __export(format_exports, {
30
31
  formatMonthYear: () => formatMonthYear,
31
32
  formatPublishedRelativeDate: () => formatPublishedRelativeDate,
32
33
  formatSalaryRange: () => formatSalaryRange,
34
+ formatSalaryStatRange: () => formatRange,
35
+ formatSalaryStatUsd: () => formatUsd,
33
36
  fullJobToCard: () => fullJobToCard,
34
37
  getSalaryLexicon: () => getSalaryLexicon,
35
38
  locationLabel: () => locationLabel,
39
+ normalizeWebsiteUrl: () => normalizeWebsiteUrl,
36
40
  resolveCustomFieldDisplay: () => resolveCustomFieldDisplay,
37
41
  uiCopy: () => uiCopy
38
42
  });
@@ -514,352 +518,46 @@ function formatSalaryRange(language, min, max, timeframe, currency = null, timef
514
518
  );
515
519
  }
516
520
 
517
- // src/format/company-intro.ts
518
- var NAMED_ENTITIES = {
519
- amp: "&",
520
- lt: "<",
521
- gt: ">",
522
- quot: '"',
523
- apos: "'",
524
- nbsp: " "
525
- };
526
- function decodeEntities(input) {
527
- return input.replace(/&(#\d+|#x[0-9a-f]+|[a-z]+);/gi, (match, body) => {
528
- const token = String(body);
529
- if (token[0] === "#") {
530
- const code = token[1] === "x" || token[1] === "X" ? parseInt(token.slice(2), 16) : parseInt(token.slice(1), 10);
531
- if (!Number.isInteger(code) || code < 0 || code > 1114111) return match;
532
- return String.fromCodePoint(code);
533
- }
534
- return NAMED_ENTITIES[token.toLowerCase()] ?? match;
521
+ // src/seo/salary.ts
522
+ var compactCurrencyCache2 = /* @__PURE__ */ new Map();
523
+ function getCompactCurrencyFormatter(language) {
524
+ const cached = compactCurrencyCache2.get(language);
525
+ if (cached) return cached;
526
+ const formatter = new Intl.NumberFormat(language, {
527
+ style: "currency",
528
+ currency: "USD",
529
+ notation: "compact",
530
+ compactDisplay: "short",
531
+ minimumFractionDigits: 0,
532
+ maximumFractionDigits: 1
535
533
  });
534
+ compactCurrencyCache2.set(language, formatter);
535
+ return formatter;
536
536
  }
537
- function companyIntro(summary, description) {
538
- const trimmedSummary = summary?.trim();
539
- if (trimmedSummary) return trimmedSummary;
540
- if (!description) return null;
541
- const text = decodeEntities(
542
- description.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<br\s*\/?>/gi, " ").replace(/<\/(p|div|li|h[1-6])>/gi, " ").replace(/<[^>]*>/g, "")
543
- ).replace(/\s+/g, " ").trim();
544
- if (!text) return null;
545
- const [first] = text.split(/(?<=[.!?])\s+/);
546
- return first ?? text;
537
+ function formatUsd(locale, value) {
538
+ try {
539
+ return getCompactCurrencyFormatter(locale).format(value);
540
+ } catch {
541
+ if (value >= 1e6) {
542
+ const millions = value / 1e6;
543
+ return `$${millions % 1 === 0 ? millions : millions.toFixed(1)}M`;
544
+ }
545
+ if (value >= 1e3) {
546
+ const thousands = value / 1e3;
547
+ return `$${thousands % 1 === 0 ? thousands : thousands.toFixed(1)}k`;
548
+ }
549
+ return `$${value}`;
550
+ }
551
+ }
552
+ function formatRange(locale, min, max) {
553
+ return `${formatUsd(locale, min)} \u2013 ${formatUsd(locale, max)}`;
547
554
  }
548
555
 
549
- // src/format/countries.ts
550
- var COUNTRY_CODES = [
551
- "AD",
552
- "AE",
553
- "AF",
554
- "AG",
555
- "AI",
556
- "AL",
557
- "AM",
558
- "AO",
559
- "AQ",
560
- "AR",
561
- "AS",
562
- "AT",
563
- "AU",
564
- "AW",
565
- "AX",
566
- "AZ",
567
- "BA",
568
- "BB",
569
- "BD",
570
- "BE",
571
- "BF",
572
- "BG",
573
- "BH",
574
- "BI",
575
- "BJ",
576
- "BL",
577
- "BM",
578
- "BN",
579
- "BO",
580
- "BQ",
581
- "BR",
582
- "BS",
583
- "BT",
584
- "BV",
585
- "BW",
586
- "BY",
587
- "BZ",
588
- "CA",
589
- "CC",
590
- "CD",
591
- "CF",
592
- "CG",
593
- "CH",
594
- "CI",
595
- "CK",
596
- "CL",
597
- "CM",
598
- "CN",
599
- "CO",
600
- "CR",
601
- "CU",
602
- "CV",
603
- "CW",
604
- "CX",
605
- "CY",
606
- "CZ",
607
- "DE",
608
- "DJ",
609
- "DK",
610
- "DM",
611
- "DO",
612
- "DZ",
613
- "EC",
614
- "EE",
615
- "EG",
616
- "EH",
617
- "ER",
618
- "ES",
619
- "ET",
620
- "FI",
621
- "FJ",
622
- "FK",
623
- "FM",
624
- "FO",
625
- "FR",
626
- "GA",
627
- "GB",
628
- "GD",
629
- "GE",
630
- "GF",
631
- "GG",
632
- "GH",
633
- "GI",
634
- "GL",
635
- "GM",
636
- "GN",
637
- "GP",
638
- "GQ",
639
- "GR",
640
- "GS",
641
- "GT",
642
- "GU",
643
- "GW",
644
- "GY",
645
- "HK",
646
- "HM",
647
- "HN",
648
- "HR",
649
- "HT",
650
- "HU",
651
- "ID",
652
- "IE",
653
- "IL",
654
- "IM",
655
- "IN",
656
- "IO",
657
- "IQ",
658
- "IR",
659
- "IS",
660
- "IT",
661
- "JE",
662
- "JM",
663
- "JO",
664
- "JP",
665
- "KE",
666
- "KG",
667
- "KH",
668
- "KI",
669
- "KM",
670
- "KN",
671
- "KR",
672
- "KW",
673
- "KY",
674
- "KZ",
675
- "LA",
676
- "LB",
677
- "LC",
678
- "LI",
679
- "LK",
680
- "LR",
681
- "LS",
682
- "LT",
683
- "LU",
684
- "LV",
685
- "LY",
686
- "MA",
687
- "MC",
688
- "MD",
689
- "ME",
690
- "MF",
691
- "MG",
692
- "MH",
693
- "MK",
694
- "ML",
695
- "MM",
696
- "MN",
697
- "MO",
698
- "MP",
699
- "MQ",
700
- "MR",
701
- "MS",
702
- "MT",
703
- "MU",
704
- "MV",
705
- "MW",
706
- "MX",
707
- "MY",
708
- "MZ",
709
- "NA",
710
- "NC",
711
- "NE",
712
- "NF",
713
- "NG",
714
- "NI",
715
- "NL",
716
- "NO",
717
- "NP",
718
- "NR",
719
- "NU",
720
- "NZ",
721
- "OM",
722
- "PA",
723
- "PE",
724
- "PF",
725
- "PG",
726
- "PH",
727
- "PK",
728
- "PL",
729
- "PM",
730
- "PN",
731
- "PR",
732
- "PS",
733
- "PT",
734
- "PW",
735
- "PY",
736
- "QA",
737
- "RE",
738
- "RO",
739
- "RS",
740
- "RU",
741
- "RW",
742
- "SA",
743
- "SB",
744
- "SC",
745
- "SD",
746
- "SE",
747
- "SG",
748
- "SH",
749
- "SI",
750
- "SJ",
751
- "SK",
752
- "SL",
753
- "SM",
754
- "SN",
755
- "SO",
756
- "SR",
757
- "SS",
758
- "ST",
759
- "SV",
760
- "SX",
761
- "SY",
762
- "SZ",
763
- "TC",
764
- "TD",
765
- "TF",
766
- "TG",
767
- "TH",
768
- "TJ",
769
- "TK",
770
- "TL",
771
- "TM",
772
- "TN",
773
- "TO",
774
- "TR",
775
- "TT",
776
- "TV",
777
- "TW",
778
- "TZ",
779
- "UA",
780
- "UG",
781
- "UM",
782
- "US",
783
- "UY",
784
- "UZ",
785
- "VA",
786
- "VC",
787
- "VE",
788
- "VG",
789
- "VI",
790
- "VN",
791
- "VU",
792
- "WF",
793
- "WS",
794
- "XK",
795
- "YE",
796
- "YT",
797
- "ZA",
798
- "ZM",
799
- "ZW"
800
- ];
801
- function regionNames(language) {
802
- try {
803
- return new Intl.DisplayNames([language], { type: "region" });
804
- } catch {
805
- return null;
806
- }
807
- }
808
- function collator(language) {
809
- try {
810
- return new Intl.Collator(language);
811
- } catch {
812
- return new Intl.Collator();
813
- }
814
- }
815
- function countryOptions(language) {
816
- const names = regionNames(language);
817
- const compare = collator(language).compare;
818
- return COUNTRY_CODES.map((code) => ({
819
- code,
820
- name: names?.of(code) ?? code
821
- })).sort((a, b) => compare(a.name, b.name));
822
- }
823
-
824
- // src/format/custom-fields.ts
825
- function renderTextValue(def, raw) {
826
- switch (def.type) {
827
- case "number":
828
- return typeof raw === "number" ? String(raw) : null;
829
- case "single_select": {
830
- if (typeof raw !== "string") return null;
831
- return def.options?.find((o) => o.key === raw)?.label ?? null;
832
- }
833
- case "multi_select": {
834
- if (!Array.isArray(raw) || raw.length === 0) return null;
835
- const labels = raw.map((key) => def.options?.find((o) => o.key === key)?.label).filter((label) => Boolean(label));
836
- return labels.length > 0 ? labels.join(", ") : null;
837
- }
838
- default:
839
- return typeof raw === "string" && raw.trim() !== "" ? raw : null;
840
- }
841
- }
842
- function resolveCustomFieldDisplay(definitions, values) {
843
- const entries = [];
844
- for (const def of definitions ?? []) {
845
- const raw = values?.[def.key];
846
- if (def.type === "boolean") {
847
- if (typeof raw === "boolean") {
848
- entries.push({
849
- key: def.key,
850
- label: def.label,
851
- kind: "boolean",
852
- value: raw
853
- });
854
- }
855
- continue;
856
- }
857
- const value = renderTextValue(def, raw);
858
- if (value !== null) {
859
- entries.push({ key: def.key, label: def.label, kind: "text", value });
860
- }
861
- }
862
- return entries;
556
+ // src/seo/job-posting.ts
557
+ function normalizeWebsiteUrl(website) {
558
+ const trimmed = website.trim();
559
+ if (!trimmed) return null;
560
+ return /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
863
561
  }
864
562
 
865
563
  // src/format/ui-copy.ts
@@ -1171,190 +869,561 @@ var DE2 = {
1171
869
  companySingular: "Unternehmen",
1172
870
  companyPlural: "Unternehmen"
1173
871
  }
1174
- };
1175
- var FR = {
1176
- jobCard: {
1177
- featuredLabel: "\xC0 la une",
1178
- aiRankedLabel: "Classement IA",
1179
- sortNewestLabel: "Plus r\xE9centes",
1180
- sortSalaryHighLabel: "Salaire : d\xE9croissant"
1181
- },
1182
- jobSearch: {
1183
- headingJobs: "Offres d'emploi",
1184
- keywordLabel: "Mot-cl\xE9",
1185
- keywordPlaceholder: "Rechercher une offre\u2026",
1186
- locationLabel: "Lieu",
1187
- locationPlaceholder: "Ville ou r\xE9gion",
1188
- workplacePlaceholder: "Mode de travail",
1189
- anyWorkplaceLabel: "Tous les modes de travail",
1190
- typePlaceholder: "Type de contrat",
1191
- anyTypeLabel: "Tous les types",
1192
- sortPlaceholder: "Trier",
1193
- loadMoreLabel: "Voir plus",
1194
- noJobsMatchText: "Aucune offre ne correspond \u2014 essayez de retirer un filtre."
1195
- },
1196
- jobDetail: {
1197
- categoriesHeading: "Cat\xE9gories",
1198
- skillsHeading: "Comp\xE9tences",
1199
- additionalDetailsHeading: "Informations compl\xE9mentaires",
1200
- customFieldYesLabel: "Oui",
1201
- customFieldNoLabel: "Non",
1202
- locationsLabel: "Lieux",
1203
- workPermitsLabel: "Permis de travail",
1204
- timezonesLabel: "Fuseaux horaires",
1205
- educationLabel: "Formation",
1206
- experienceLabel: "Exp\xE9rience",
1207
- worldwideLabel: "Monde entier",
1208
- noExperienceRequiredLabel: "Aucune exp\xE9rience requise",
1209
- experienceYears: (years) => `${years}+ ans`,
1210
- posted: (published) => `Publi\xE9e ${published}`,
1211
- noDescriptionText: "Aucune description fournie.",
1212
- viewCompanyProfileLabel: "Voir le profil de l'entreprise",
1213
- similarJobsHeading: "Offres similaires",
1214
- breadcrumbAriaLabel: "Fil d'Ariane"
1215
- },
1216
- apply: {
1217
- applyButtonText: "Postuler \xE0 cette offre",
1218
- applyingLabel: "Envoi en cours\u2026",
1219
- applyOnEmployerSiteLabel: "Postuler sur le site de l'employeur",
1220
- signInToApplyLabel: "Connectez-vous pour postuler",
1221
- verifyEmailToApplyLabel: "V\xE9rifiez votre e-mail pour postuler",
1222
- appliedViewApplicationsLabel: "Candidature envoy\xE9e \u2014 voir mes candidatures",
1223
- applicationSubmitError: "Une erreur est survenue. Veuillez r\xE9essayer."
1224
- },
1225
- alerts: {
1226
- jobAlertTitle: "Recevoir des alertes emploi",
1227
- jobAlertEmailPlaceholder: "vous@exemple.fr",
1228
- jobAlertButtonText: "Activer les alertes",
1229
- subscribingLabel: "Abonnement en cours\u2026",
1230
- jobAlertSuccessToast: "Si cet e-mail n'est pas d\xE9j\xE0 abonn\xE9, nous avons envoy\xE9 un lien de confirmation \u2014 v\xE9rifiez votre bo\xEEte de r\xE9ception.",
1231
- jobAlertErrorToast: "Une erreur est survenue. Veuillez r\xE9essayer.",
1232
- sectionAriaLabel: "Alertes emploi",
1233
- emailAriaLabel: "e-mail",
1234
- submitAriaLabel: "recevoir des alertes emploi"
1235
- },
1236
- copyLink: {
1237
- copyLinkLabel: "Copier le lien",
1238
- copiedLabel: "Copi\xE9",
1239
- ariaLabel: "copier le lien"
1240
- },
1241
- salary: {
1242
- comparisonHeadlineAverage: "Salaire moyen",
1243
- perYearSuffix: "/ an",
1244
- comparisonPercentile25Label: "25e percentile",
1245
- medianLabel: "M\xE9diane",
1246
- comparisonPercentile75Label: "75e percentile",
1247
- basedOnLabel: "Bas\xE9 sur",
1248
- seniorityTableHeaderLevel: "Niveau",
1249
- seniorityTableHeaderAvg: "Moyenne",
1250
- boardBaselineLabel: "R\xE9f\xE9rence du site",
1251
- seniorityTableHeaderDiff: "vs site",
1252
- faqHeading: "Questions fr\xE9quentes"
1253
- },
1254
- nav: {
1255
- home: "Offres",
1256
- companies: "Entreprises",
1257
- pricing: "Tarifs",
1258
- talent: "Talents",
1259
- post: "Publier une offre",
1260
- blog: "Blog"
1261
- },
1262
- footer: {
1263
- forCandidatesHeading: "Pour les candidats",
1264
- forCompaniesHeading: "Pour les entreprises",
1265
- resourcesHeading: "Ressources",
1266
- aboutHeading: "\xC0 propos",
1267
- websiteLabel: "Site web",
1268
- aboutLabel: "\xC0 propos",
1269
- contactLabel: "Nous contacter",
1270
- locationsLabel: "Lieux",
1271
- salariesLabel: "Salaires",
1272
- sitemapLabel: "Plan du site",
1273
- termsOfServiceLabel: "Conditions d'utilisation",
1274
- privacyPolicyLabel: "Politique de confidentialit\xE9",
1275
- cookiePolicyLabel: "Politique de cookies",
1276
- impressumLabel: "Mentions l\xE9gales",
1277
- allRightsReservedText: "Tous droits r\xE9serv\xE9s.",
1278
- copyrightPrefix: "\xA9 {{year}} {{board_name}}.",
1279
- poweredByText: "Propuls\xE9 par",
1280
- defaultDescription: "D\xE9couvrez les derni\xE8res offres de {{board_name}}."
1281
- },
1282
- breadcrumbs: {
1283
- home: "Accueil",
1284
- jobs: "Offres",
1285
- locations: "Lieux",
1286
- salaries: "Salaires",
1287
- companies: "Entreprises",
1288
- skills: "Comp\xE9tences",
1289
- titles: "Intitul\xE9s de poste",
1290
- blog: "Blog",
1291
- post: "Publier une offre",
1292
- pricing: "Tarifs",
1293
- about: "\xC0 propos",
1294
- impressum: "Mentions l\xE9gales",
1295
- termsOfService: "Conditions d'utilisation",
1296
- privacyPolicy: "Politique de confidentialit\xE9",
1297
- cookiePolicy: "Politique de cookies",
1298
- talent: "Talents"
1299
- },
1300
- pagination: {
1301
- ariaLabel: "Pagination",
1302
- previousLabel: "Pr\xE9c\xE9dent",
1303
- nextLabel: "Suivant"
1304
- },
1305
- blog: {
1306
- bylineLabel: "Par",
1307
- readingTimeLabel: "min de lecture",
1308
- searchLabel: "Recherche",
1309
- searchPlaceholder: "Rechercher un article...",
1310
- clearButtonLabel: "Effacer la recherche",
1311
- tagFilterLabel: "Th\xE8mes",
1312
- tagFilterAllLabel: "Tous",
1313
- emptyTitle: "Aucun article correspondant",
1314
- emptyDescription: "Essayez de modifier votre recherche ou de r\xE9initialiser les filtres.",
1315
- emptyResetLabel: "R\xE9initialiser les filtres"
1316
- },
1317
- entity: {
1318
- jobSingular: "offre",
1319
- jobPlural: "offres",
1320
- companySingular: "entreprise",
1321
- companyPlural: "entreprises"
872
+ };
873
+ var FR = {
874
+ jobCard: {
875
+ featuredLabel: "\xC0 la une",
876
+ aiRankedLabel: "Classement IA",
877
+ sortNewestLabel: "Plus r\xE9centes",
878
+ sortSalaryHighLabel: "Salaire : d\xE9croissant"
879
+ },
880
+ jobSearch: {
881
+ headingJobs: "Offres d'emploi",
882
+ keywordLabel: "Mot-cl\xE9",
883
+ keywordPlaceholder: "Rechercher une offre\u2026",
884
+ locationLabel: "Lieu",
885
+ locationPlaceholder: "Ville ou r\xE9gion",
886
+ workplacePlaceholder: "Mode de travail",
887
+ anyWorkplaceLabel: "Tous les modes de travail",
888
+ typePlaceholder: "Type de contrat",
889
+ anyTypeLabel: "Tous les types",
890
+ sortPlaceholder: "Trier",
891
+ loadMoreLabel: "Voir plus",
892
+ noJobsMatchText: "Aucune offre ne correspond \u2014 essayez de retirer un filtre."
893
+ },
894
+ jobDetail: {
895
+ categoriesHeading: "Cat\xE9gories",
896
+ skillsHeading: "Comp\xE9tences",
897
+ additionalDetailsHeading: "Informations compl\xE9mentaires",
898
+ customFieldYesLabel: "Oui",
899
+ customFieldNoLabel: "Non",
900
+ locationsLabel: "Lieux",
901
+ workPermitsLabel: "Permis de travail",
902
+ timezonesLabel: "Fuseaux horaires",
903
+ educationLabel: "Formation",
904
+ experienceLabel: "Exp\xE9rience",
905
+ worldwideLabel: "Monde entier",
906
+ noExperienceRequiredLabel: "Aucune exp\xE9rience requise",
907
+ experienceYears: (years) => `${years}+ ans`,
908
+ posted: (published) => `Publi\xE9e ${published}`,
909
+ noDescriptionText: "Aucune description fournie.",
910
+ viewCompanyProfileLabel: "Voir le profil de l'entreprise",
911
+ similarJobsHeading: "Offres similaires",
912
+ breadcrumbAriaLabel: "Fil d'Ariane"
913
+ },
914
+ apply: {
915
+ applyButtonText: "Postuler \xE0 cette offre",
916
+ applyingLabel: "Envoi en cours\u2026",
917
+ applyOnEmployerSiteLabel: "Postuler sur le site de l'employeur",
918
+ signInToApplyLabel: "Connectez-vous pour postuler",
919
+ verifyEmailToApplyLabel: "V\xE9rifiez votre e-mail pour postuler",
920
+ appliedViewApplicationsLabel: "Candidature envoy\xE9e \u2014 voir mes candidatures",
921
+ applicationSubmitError: "Une erreur est survenue. Veuillez r\xE9essayer."
922
+ },
923
+ alerts: {
924
+ jobAlertTitle: "Recevoir des alertes emploi",
925
+ jobAlertEmailPlaceholder: "vous@exemple.fr",
926
+ jobAlertButtonText: "Activer les alertes",
927
+ subscribingLabel: "Abonnement en cours\u2026",
928
+ jobAlertSuccessToast: "Si cet e-mail n'est pas d\xE9j\xE0 abonn\xE9, nous avons envoy\xE9 un lien de confirmation \u2014 v\xE9rifiez votre bo\xEEte de r\xE9ception.",
929
+ jobAlertErrorToast: "Une erreur est survenue. Veuillez r\xE9essayer.",
930
+ sectionAriaLabel: "Alertes emploi",
931
+ emailAriaLabel: "e-mail",
932
+ submitAriaLabel: "recevoir des alertes emploi"
933
+ },
934
+ copyLink: {
935
+ copyLinkLabel: "Copier le lien",
936
+ copiedLabel: "Copi\xE9",
937
+ ariaLabel: "copier le lien"
938
+ },
939
+ salary: {
940
+ comparisonHeadlineAverage: "Salaire moyen",
941
+ perYearSuffix: "/ an",
942
+ comparisonPercentile25Label: "25e percentile",
943
+ medianLabel: "M\xE9diane",
944
+ comparisonPercentile75Label: "75e percentile",
945
+ basedOnLabel: "Bas\xE9 sur",
946
+ seniorityTableHeaderLevel: "Niveau",
947
+ seniorityTableHeaderAvg: "Moyenne",
948
+ boardBaselineLabel: "R\xE9f\xE9rence du site",
949
+ seniorityTableHeaderDiff: "vs site",
950
+ faqHeading: "Questions fr\xE9quentes"
951
+ },
952
+ nav: {
953
+ home: "Offres",
954
+ companies: "Entreprises",
955
+ pricing: "Tarifs",
956
+ talent: "Talents",
957
+ post: "Publier une offre",
958
+ blog: "Blog"
959
+ },
960
+ footer: {
961
+ forCandidatesHeading: "Pour les candidats",
962
+ forCompaniesHeading: "Pour les entreprises",
963
+ resourcesHeading: "Ressources",
964
+ aboutHeading: "\xC0 propos",
965
+ websiteLabel: "Site web",
966
+ aboutLabel: "\xC0 propos",
967
+ contactLabel: "Nous contacter",
968
+ locationsLabel: "Lieux",
969
+ salariesLabel: "Salaires",
970
+ sitemapLabel: "Plan du site",
971
+ termsOfServiceLabel: "Conditions d'utilisation",
972
+ privacyPolicyLabel: "Politique de confidentialit\xE9",
973
+ cookiePolicyLabel: "Politique de cookies",
974
+ impressumLabel: "Mentions l\xE9gales",
975
+ allRightsReservedText: "Tous droits r\xE9serv\xE9s.",
976
+ copyrightPrefix: "\xA9 {{year}} {{board_name}}.",
977
+ poweredByText: "Propuls\xE9 par",
978
+ defaultDescription: "D\xE9couvrez les derni\xE8res offres de {{board_name}}."
979
+ },
980
+ breadcrumbs: {
981
+ home: "Accueil",
982
+ jobs: "Offres",
983
+ locations: "Lieux",
984
+ salaries: "Salaires",
985
+ companies: "Entreprises",
986
+ skills: "Comp\xE9tences",
987
+ titles: "Intitul\xE9s de poste",
988
+ blog: "Blog",
989
+ post: "Publier une offre",
990
+ pricing: "Tarifs",
991
+ about: "\xC0 propos",
992
+ impressum: "Mentions l\xE9gales",
993
+ termsOfService: "Conditions d'utilisation",
994
+ privacyPolicy: "Politique de confidentialit\xE9",
995
+ cookiePolicy: "Politique de cookies",
996
+ talent: "Talents"
997
+ },
998
+ pagination: {
999
+ ariaLabel: "Pagination",
1000
+ previousLabel: "Pr\xE9c\xE9dent",
1001
+ nextLabel: "Suivant"
1002
+ },
1003
+ blog: {
1004
+ bylineLabel: "Par",
1005
+ readingTimeLabel: "min de lecture",
1006
+ searchLabel: "Recherche",
1007
+ searchPlaceholder: "Rechercher un article...",
1008
+ clearButtonLabel: "Effacer la recherche",
1009
+ tagFilterLabel: "Th\xE8mes",
1010
+ tagFilterAllLabel: "Tous",
1011
+ emptyTitle: "Aucun article correspondant",
1012
+ emptyDescription: "Essayez de modifier votre recherche ou de r\xE9initialiser les filtres.",
1013
+ emptyResetLabel: "R\xE9initialiser les filtres"
1014
+ },
1015
+ entity: {
1016
+ jobSingular: "offre",
1017
+ jobPlural: "offres",
1018
+ companySingular: "entreprise",
1019
+ companyPlural: "entreprises"
1020
+ }
1021
+ };
1022
+ var CATALOGS = { en: EN2, de: DE2, fr: FR };
1023
+ function mergeGroup(group, overrides) {
1024
+ if (!overrides) return group;
1025
+ const out = { ...group };
1026
+ for (const [key, value] of Object.entries(group)) {
1027
+ if (typeof value !== "string") continue;
1028
+ const override = overrides[key];
1029
+ if (typeof override === "string" && override.trim() !== "") {
1030
+ out[key] = override;
1031
+ }
1032
+ }
1033
+ return out;
1034
+ }
1035
+ var GROUP_OVERRIDE_SOURCE = {
1036
+ jobCard: "jobCardLabels",
1037
+ jobSearch: "jobSearchLabels",
1038
+ jobDetail: "jobCardLabels",
1039
+ apply: "jobCardLabels",
1040
+ alerts: "jobCardLabels",
1041
+ copyLink: "jobCardLabels",
1042
+ salary: "salaryLabels",
1043
+ nav: "navLabels",
1044
+ footer: "footerLabels",
1045
+ breadcrumbs: "breadcrumbsLabels",
1046
+ pagination: "globalPaginationLabels",
1047
+ blog: "blogSharedLabels",
1048
+ entity: "entityLabels"
1049
+ };
1050
+ function uiCopy(language, labels) {
1051
+ const catalog = language && CATALOGS[language] || EN2;
1052
+ if (!labels) return catalog;
1053
+ const resolved = {};
1054
+ for (const [group, source] of Object.entries(GROUP_OVERRIDE_SOURCE)) {
1055
+ resolved[group] = mergeGroup(catalog[group], labels[source]);
1056
+ }
1057
+ return resolved;
1058
+ }
1059
+
1060
+ // src/seo/breadcrumbs.ts
1061
+ function buildJobBreadcrumbs(job, language, labels) {
1062
+ const copy = uiCopy(language, labels).breadcrumbs;
1063
+ const crumbs = [
1064
+ { name: copy.home, path: "/" },
1065
+ { name: copy.jobs, path: "/jobs" }
1066
+ ];
1067
+ let lastPlaceSlug = null;
1068
+ for (const place of job.placeHierarchy) {
1069
+ lastPlaceSlug = place.slug;
1070
+ crumbs.push({ name: place.name, path: `/jobs/locations/${place.slug}` });
1071
+ }
1072
+ const primaryCategory = job.categories[0];
1073
+ if (primaryCategory) {
1074
+ crumbs.push({
1075
+ name: primaryCategory.name,
1076
+ path: lastPlaceSlug ? `/jobs/locations/${lastPlaceSlug}/${primaryCategory.slug}` : `/jobs/${primaryCategory.slug}`
1077
+ });
1078
+ }
1079
+ crumbs.push({ name: job.title });
1080
+ return crumbs;
1081
+ }
1082
+
1083
+ // src/format/company-intro.ts
1084
+ var NAMED_ENTITIES = {
1085
+ amp: "&",
1086
+ lt: "<",
1087
+ gt: ">",
1088
+ quot: '"',
1089
+ apos: "'",
1090
+ nbsp: " "
1091
+ };
1092
+ function decodeEntities(input) {
1093
+ return input.replace(/&(#\d+|#x[0-9a-f]+|[a-z]+);/gi, (match, body) => {
1094
+ const token = String(body);
1095
+ if (token[0] === "#") {
1096
+ const code = token[1] === "x" || token[1] === "X" ? parseInt(token.slice(2), 16) : parseInt(token.slice(1), 10);
1097
+ if (!Number.isInteger(code) || code < 0 || code > 1114111) return match;
1098
+ return String.fromCodePoint(code);
1099
+ }
1100
+ return NAMED_ENTITIES[token.toLowerCase()] ?? match;
1101
+ });
1102
+ }
1103
+ function companyIntro(summary, description) {
1104
+ const trimmedSummary = summary?.trim();
1105
+ if (trimmedSummary) return trimmedSummary;
1106
+ if (!description) return null;
1107
+ const text = decodeEntities(
1108
+ description.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<br\s*\/?>/gi, " ").replace(/<\/(p|div|li|h[1-6])>/gi, " ").replace(/<[^>]*>/g, "")
1109
+ ).replace(/\s+/g, " ").trim();
1110
+ if (!text) return null;
1111
+ const [first] = text.split(/(?<=[.!?])\s+/);
1112
+ return first ?? text;
1113
+ }
1114
+
1115
+ // src/format/countries.ts
1116
+ var COUNTRY_CODES = [
1117
+ "AD",
1118
+ "AE",
1119
+ "AF",
1120
+ "AG",
1121
+ "AI",
1122
+ "AL",
1123
+ "AM",
1124
+ "AO",
1125
+ "AQ",
1126
+ "AR",
1127
+ "AS",
1128
+ "AT",
1129
+ "AU",
1130
+ "AW",
1131
+ "AX",
1132
+ "AZ",
1133
+ "BA",
1134
+ "BB",
1135
+ "BD",
1136
+ "BE",
1137
+ "BF",
1138
+ "BG",
1139
+ "BH",
1140
+ "BI",
1141
+ "BJ",
1142
+ "BL",
1143
+ "BM",
1144
+ "BN",
1145
+ "BO",
1146
+ "BQ",
1147
+ "BR",
1148
+ "BS",
1149
+ "BT",
1150
+ "BV",
1151
+ "BW",
1152
+ "BY",
1153
+ "BZ",
1154
+ "CA",
1155
+ "CC",
1156
+ "CD",
1157
+ "CF",
1158
+ "CG",
1159
+ "CH",
1160
+ "CI",
1161
+ "CK",
1162
+ "CL",
1163
+ "CM",
1164
+ "CN",
1165
+ "CO",
1166
+ "CR",
1167
+ "CU",
1168
+ "CV",
1169
+ "CW",
1170
+ "CX",
1171
+ "CY",
1172
+ "CZ",
1173
+ "DE",
1174
+ "DJ",
1175
+ "DK",
1176
+ "DM",
1177
+ "DO",
1178
+ "DZ",
1179
+ "EC",
1180
+ "EE",
1181
+ "EG",
1182
+ "EH",
1183
+ "ER",
1184
+ "ES",
1185
+ "ET",
1186
+ "FI",
1187
+ "FJ",
1188
+ "FK",
1189
+ "FM",
1190
+ "FO",
1191
+ "FR",
1192
+ "GA",
1193
+ "GB",
1194
+ "GD",
1195
+ "GE",
1196
+ "GF",
1197
+ "GG",
1198
+ "GH",
1199
+ "GI",
1200
+ "GL",
1201
+ "GM",
1202
+ "GN",
1203
+ "GP",
1204
+ "GQ",
1205
+ "GR",
1206
+ "GS",
1207
+ "GT",
1208
+ "GU",
1209
+ "GW",
1210
+ "GY",
1211
+ "HK",
1212
+ "HM",
1213
+ "HN",
1214
+ "HR",
1215
+ "HT",
1216
+ "HU",
1217
+ "ID",
1218
+ "IE",
1219
+ "IL",
1220
+ "IM",
1221
+ "IN",
1222
+ "IO",
1223
+ "IQ",
1224
+ "IR",
1225
+ "IS",
1226
+ "IT",
1227
+ "JE",
1228
+ "JM",
1229
+ "JO",
1230
+ "JP",
1231
+ "KE",
1232
+ "KG",
1233
+ "KH",
1234
+ "KI",
1235
+ "KM",
1236
+ "KN",
1237
+ "KR",
1238
+ "KW",
1239
+ "KY",
1240
+ "KZ",
1241
+ "LA",
1242
+ "LB",
1243
+ "LC",
1244
+ "LI",
1245
+ "LK",
1246
+ "LR",
1247
+ "LS",
1248
+ "LT",
1249
+ "LU",
1250
+ "LV",
1251
+ "LY",
1252
+ "MA",
1253
+ "MC",
1254
+ "MD",
1255
+ "ME",
1256
+ "MF",
1257
+ "MG",
1258
+ "MH",
1259
+ "MK",
1260
+ "ML",
1261
+ "MM",
1262
+ "MN",
1263
+ "MO",
1264
+ "MP",
1265
+ "MQ",
1266
+ "MR",
1267
+ "MS",
1268
+ "MT",
1269
+ "MU",
1270
+ "MV",
1271
+ "MW",
1272
+ "MX",
1273
+ "MY",
1274
+ "MZ",
1275
+ "NA",
1276
+ "NC",
1277
+ "NE",
1278
+ "NF",
1279
+ "NG",
1280
+ "NI",
1281
+ "NL",
1282
+ "NO",
1283
+ "NP",
1284
+ "NR",
1285
+ "NU",
1286
+ "NZ",
1287
+ "OM",
1288
+ "PA",
1289
+ "PE",
1290
+ "PF",
1291
+ "PG",
1292
+ "PH",
1293
+ "PK",
1294
+ "PL",
1295
+ "PM",
1296
+ "PN",
1297
+ "PR",
1298
+ "PS",
1299
+ "PT",
1300
+ "PW",
1301
+ "PY",
1302
+ "QA",
1303
+ "RE",
1304
+ "RO",
1305
+ "RS",
1306
+ "RU",
1307
+ "RW",
1308
+ "SA",
1309
+ "SB",
1310
+ "SC",
1311
+ "SD",
1312
+ "SE",
1313
+ "SG",
1314
+ "SH",
1315
+ "SI",
1316
+ "SJ",
1317
+ "SK",
1318
+ "SL",
1319
+ "SM",
1320
+ "SN",
1321
+ "SO",
1322
+ "SR",
1323
+ "SS",
1324
+ "ST",
1325
+ "SV",
1326
+ "SX",
1327
+ "SY",
1328
+ "SZ",
1329
+ "TC",
1330
+ "TD",
1331
+ "TF",
1332
+ "TG",
1333
+ "TH",
1334
+ "TJ",
1335
+ "TK",
1336
+ "TL",
1337
+ "TM",
1338
+ "TN",
1339
+ "TO",
1340
+ "TR",
1341
+ "TT",
1342
+ "TV",
1343
+ "TW",
1344
+ "TZ",
1345
+ "UA",
1346
+ "UG",
1347
+ "UM",
1348
+ "US",
1349
+ "UY",
1350
+ "UZ",
1351
+ "VA",
1352
+ "VC",
1353
+ "VE",
1354
+ "VG",
1355
+ "VI",
1356
+ "VN",
1357
+ "VU",
1358
+ "WF",
1359
+ "WS",
1360
+ "XK",
1361
+ "YE",
1362
+ "YT",
1363
+ "ZA",
1364
+ "ZM",
1365
+ "ZW"
1366
+ ];
1367
+ function regionNames(language) {
1368
+ try {
1369
+ return new Intl.DisplayNames([language], { type: "region" });
1370
+ } catch {
1371
+ return null;
1372
+ }
1373
+ }
1374
+ function collator(language) {
1375
+ try {
1376
+ return new Intl.Collator(language);
1377
+ } catch {
1378
+ return new Intl.Collator();
1322
1379
  }
1323
- };
1324
- var CATALOGS = { en: EN2, de: DE2, fr: FR };
1325
- function mergeGroup(group, overrides) {
1326
- if (!overrides) return group;
1327
- const out = { ...group };
1328
- for (const [key, value] of Object.entries(group)) {
1329
- if (typeof value !== "string") continue;
1330
- const override = overrides[key];
1331
- if (typeof override === "string" && override.trim() !== "") {
1332
- out[key] = override;
1380
+ }
1381
+ function countryOptions(language) {
1382
+ const names = regionNames(language);
1383
+ const compare = collator(language).compare;
1384
+ return COUNTRY_CODES.map((code) => ({
1385
+ code,
1386
+ name: names?.of(code) ?? code
1387
+ })).sort((a, b) => compare(a.name, b.name));
1388
+ }
1389
+
1390
+ // src/format/custom-fields.ts
1391
+ function renderTextValue(def, raw) {
1392
+ switch (def.type) {
1393
+ case "number":
1394
+ return typeof raw === "number" ? String(raw) : null;
1395
+ case "single_select": {
1396
+ if (typeof raw !== "string") return null;
1397
+ return def.options?.find((o) => o.key === raw)?.label ?? null;
1398
+ }
1399
+ case "multi_select": {
1400
+ if (!Array.isArray(raw) || raw.length === 0) return null;
1401
+ const labels = raw.map((key) => def.options?.find((o) => o.key === key)?.label).filter((label) => Boolean(label));
1402
+ return labels.length > 0 ? labels.join(", ") : null;
1333
1403
  }
1404
+ default:
1405
+ return typeof raw === "string" && raw.trim() !== "" ? raw : null;
1334
1406
  }
1335
- return out;
1336
1407
  }
1337
- var GROUP_OVERRIDE_SOURCE = {
1338
- jobCard: "jobCardLabels",
1339
- jobSearch: "jobSearchLabels",
1340
- jobDetail: "jobCardLabels",
1341
- apply: "jobCardLabels",
1342
- alerts: "jobCardLabels",
1343
- copyLink: "jobCardLabels",
1344
- salary: "salaryLabels",
1345
- nav: "navLabels",
1346
- footer: "footerLabels",
1347
- breadcrumbs: "breadcrumbsLabels",
1348
- pagination: "globalPaginationLabels",
1349
- blog: "blogSharedLabels",
1350
- entity: "entityLabels"
1351
- };
1352
- function uiCopy(language, labels) {
1353
- const catalog = language && CATALOGS[language] || EN2;
1354
- if (!labels) return catalog;
1355
- const resolved = {};
1356
- for (const [group, source] of Object.entries(GROUP_OVERRIDE_SOURCE)) {
1357
- resolved[group] = mergeGroup(catalog[group], labels[source]);
1408
+ function resolveCustomFieldDisplay(definitions, values) {
1409
+ const entries = [];
1410
+ for (const def of definitions ?? []) {
1411
+ const raw = values?.[def.key];
1412
+ if (def.type === "boolean") {
1413
+ if (typeof raw === "boolean") {
1414
+ entries.push({
1415
+ key: def.key,
1416
+ label: def.label,
1417
+ kind: "boolean",
1418
+ value: raw
1419
+ });
1420
+ }
1421
+ continue;
1422
+ }
1423
+ const value = renderTextValue(def, raw);
1424
+ if (value !== null) {
1425
+ entries.push({ key: def.key, label: def.label, kind: "text", value });
1426
+ }
1358
1427
  }
1359
- return resolved;
1428
+ return entries;
1360
1429
  }