@thejob/schema 2.0.3 → 2.0.5

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/index.js CHANGED
@@ -645,6 +645,13 @@ var JobSchema = object16({
645
645
  * re-check cadence so the same job is not re-crawled on every sweep.
646
646
  */
647
647
  lastExpiryCheckAt: number5().optional().nullable().label("Last expiry check at"),
648
+ /**
649
+ * When this job transitioned to `status: closed` (epoch ms). Server-owned
650
+ * operational field: stamped by every jobs-service close path (expiry
651
+ * auto-close and the dated lapse sweep). Distinct from `updatedAt` so a later
652
+ * edit to a closed job does not move it.
653
+ */
654
+ closedAt: number5().optional().nullable().label("Closed at"),
648
655
  experienceLevel: string13().oneOf(SupportedExperienceLevels).nullable().optional().label("Experience level"),
649
656
  contactEmail: string13().email().nullable().optional().label("Contact email"),
650
657
  workingHoursPerWeek: number5().nullable().optional().min(1).max(24 * 7).label("Working hours per week"),
@@ -674,6 +681,20 @@ var JobSchema = object16({
674
681
  keywords: array8().of(string13()).optional().label("Keywords")
675
682
  }).nullable().optional().label("SEO Tags"),
676
683
  jdSummary: string13().optional().label("JD Summary"),
684
+ /**
685
+ * Structured, model-extracted sections of the posting, re-organized into clean
686
+ * bullet points. This is our OWN unique presentation of the job (rendered as the
687
+ * primary, server-side page content) so the page adds value over the raw scraped
688
+ * description and is not penalized by Google as duplicate content. All sections
689
+ * optional: absent on jobs not yet (re-)enriched, and any section is `[]`/omitted
690
+ * when the posting genuinely has none.
691
+ */
692
+ jobHighlights: object16({
693
+ responsibilities: array8().of(string13()).optional().label("Responsibilities"),
694
+ requirements: array8().of(string13()).optional().label("Requirements"),
695
+ benefits: array8().of(string13()).optional().label("Benefits"),
696
+ qualifications: array8().of(string13()).optional().label("Qualifications")
697
+ }).nullable().optional().label("Job Highlights"),
677
698
  canCreateAlert: boolean9().optional().label("Can create alert"),
678
699
  canDirectApply: boolean9().optional().label("Can direct apply"),
679
700
  hasApplied: boolean9().optional().label("Has applied"),
@@ -699,15 +720,65 @@ var JobSchema = object16({
699
720
  featuredMarkets: array8().of(string13().required()).optional().default([]).label("Featured markets")
700
721
  }).concat(DbDefaultSchema).noUnknown().label("Job");
701
722
 
723
+ // src/post/post.schema.ts
724
+ import { array as array9, boolean as boolean10, number as number6, object as object17, string as string14 } from "yup";
725
+
726
+ // src/post/post.constant.ts
727
+ var PostStatus = /* @__PURE__ */ ((PostStatus2) => {
728
+ PostStatus2["Draft"] = "draft";
729
+ PostStatus2["Published"] = "published";
730
+ PostStatus2["Archived"] = "archived";
731
+ return PostStatus2;
732
+ })(PostStatus || {});
733
+ var SupportedPostStatuses = Object.values(PostStatus);
734
+
735
+ // src/post/post.schema.ts
736
+ var PostSchema = object17({
737
+ /** Headline shown in listings and at the top of the post. */
738
+ title: string14().required().min(5).max(200).label("Title"),
739
+ /** User-friendly, unique URL identifier. */
740
+ slug: string14().required().max(250).label("Slug"),
741
+ /** Short summary used in listings and meta description fallback. */
742
+ excerpt: string14().max(500).optional().label("Excerpt"),
743
+ /** Rendered HTML source of truth for the post body (sanitized on write/render).
744
+ * Supports rich formatting markdown can't express (image size/alignment, etc). */
745
+ bodyHTML: string14().required().min(1).label("Body (HTML)"),
746
+ /** Optional Markdown mirror of the body (portable text / search fallback).
747
+ * Kept for back-compat and plain-text derivation; bodyHTML is authoritative. */
748
+ bodyMarkdown: string14().optional().label("Body (Markdown)"),
749
+ /** Cover/hero image URL. Not `.url()`-validated: the value is either an
750
+ * uploaded file URL served by this platform (which may be a bare host like
751
+ * `http://localhost:3082/...` in dev, that yup's `.url()` wrongly rejects) or
752
+ * an admin-pasted link. Kept as a plain string; empty ⇒ null. */
753
+ coverImage: string14().nullable().optional().label("Cover image"),
754
+ /** Author (user id). */
755
+ authorId: string14().required().label("Author ID"),
756
+ /** Free-form tags for filtering and related-posts. */
757
+ tags: array9().of(string14().trim().required()).default([]).label("Tags"),
758
+ /** Estimated reading time in minutes (server-derived from body length). */
759
+ readingTimeMinutes: number6().min(0).optional().label("Reading time (minutes)"),
760
+ /** SEO overrides. */
761
+ seoTags: object17({
762
+ description: string14().optional().label("Description"),
763
+ keywords: array9().of(string14().required()).optional().label("Keywords")
764
+ }).nullable().optional().label("SEO Tags"),
765
+ /** When the post was first published (epoch ms). Server-owned. */
766
+ publishedAt: number6().nullable().optional().label("Published at"),
767
+ /** Whether the post is featured in the blog listing. */
768
+ isFeatured: boolean10().default(false).optional().label("Is featured"),
769
+ /** Publication state. */
770
+ status: string14().oneOf(SupportedPostStatuses).default("draft" /* Draft */).required().label("Status")
771
+ }).concat(DbDefaultSchema).noUnknown().label("Post");
772
+
702
773
  // src/pagination/pagination.schema.ts
703
- import { number as number6, object as object17, string as string14 } from "yup";
704
- var PaginationSchema = object17().shape({
705
- page: number6().required().min(1).label("Page").default(1),
706
- limit: number6().required().min(1).max(100).label("Limit").default(10),
707
- sortBy: string14().notOneOf([""]).optional().label("SortBy"),
708
- nextPage: string14().optional().label("Next Page Token"),
709
- prevPage: string14().optional().label("Previous Page Token"),
710
- sortDirection: string14().oneOf(["asc", "desc"]).optional().label("Sort Direction")
774
+ import { number as number7, object as object18, string as string15 } from "yup";
775
+ var PaginationSchema = object18().shape({
776
+ page: number7().required().min(1).label("Page").default(1),
777
+ limit: number7().required().min(1).max(100).label("Limit").default(10),
778
+ sortBy: string15().notOneOf([""]).optional().label("SortBy"),
779
+ nextPage: string15().optional().label("Next Page Token"),
780
+ prevPage: string15().optional().label("Previous Page Token"),
781
+ sortDirection: string15().oneOf(["asc", "desc"]).optional().label("Sort Direction")
711
782
  });
712
783
 
713
784
  // src/pagination/pagination.constant.ts
@@ -723,7 +794,7 @@ var DefaultPaginationOptions = {
723
794
  };
724
795
 
725
796
  // src/report/report.schema.ts
726
- import { mixed as mixed4, object as object18, string as string15 } from "yup";
797
+ import { mixed as mixed4, object as object19, string as string16 } from "yup";
727
798
 
728
799
  // src/report/report.constant.ts
729
800
  var ResourceType = /* @__PURE__ */ ((ResourceType3) => {
@@ -747,15 +818,15 @@ var ReportReason = /* @__PURE__ */ ((ReportReason3) => {
747
818
  var SupportedReportReasons = Object.values(ReportReason);
748
819
 
749
820
  // src/report/report.schema.ts
750
- var ReportSchema = object18({
821
+ var ReportSchema = object19({
751
822
  type: mixed4().oneOf(SupportedResourceTypes).required().label("Type"),
752
- resourceId: string15().required().label("Resource ID"),
823
+ resourceId: string16().required().label("Resource ID"),
753
824
  reason: mixed4().oneOf(SupportedReportReasons).required("Please choose a reason").label("Reason"),
754
- comment: string15().max(1e3).optional().label("Comment")
825
+ comment: string16().max(1e3).optional().label("Comment")
755
826
  }).label("Report Schema");
756
827
 
757
828
  // src/user/user.schema.ts
758
- import { array as array12, number as number8, object as object30, string as string28 } from "yup";
829
+ import { array as array13, number as number9, object as object31, string as string29 } from "yup";
759
830
 
760
831
  // src/user/user.constant.ts
761
832
  var UserRole = /* @__PURE__ */ ((UserRole2) => {
@@ -837,51 +908,51 @@ var MIN_SALARY_LOWER_BOUND = 0;
837
908
  var MIN_SALARY_UPPER_BOUND = 1e6;
838
909
 
839
910
  // src/user/work-experience.schema.ts
840
- import { boolean as boolean10, object as object19, string as string16 } from "yup";
841
- var WorkExperienceSchema = object19().shape({
911
+ import { boolean as boolean11, object as object20, string as string17 } from "yup";
912
+ var WorkExperienceSchema = object20().shape({
842
913
  company: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Company"),
843
- designation: string16().trim().required().label("Designation"),
914
+ designation: string17().trim().required().label("Designation"),
844
915
  duration: DurationSchema({
845
916
  format: "YYYY-MM",
846
917
  startLabel: "Start Date",
847
918
  endLabel: "End Date"
848
919
  }).required().label("Duration"),
849
- description: string16().trim().max(3e3).optional().label("Description"),
920
+ description: string17().trim().max(3e3).optional().label("Description"),
850
921
  location: LocationSchema.required().label("Location"),
851
- isRemote: boolean10().oneOf([true, false]).default(false).label("Is Remote")
922
+ isRemote: boolean11().oneOf([true, false]).default(false).label("Is Remote")
852
923
  }).noUnknown().strict().label("Work Experience");
853
924
 
854
925
  // src/user/education.schema.ts
855
- import { boolean as boolean11, object as object20, string as string17 } from "yup";
856
- var EducationSchema = object20({
926
+ import { boolean as boolean12, object as object21, string as string18 } from "yup";
927
+ var EducationSchema = object21({
857
928
  institute: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Institute"),
858
- course: string17().trim().required().label("Course"),
859
- fieldOfStudy: string17().trim().required().label("Field of Study"),
929
+ course: string18().trim().required().label("Course"),
930
+ fieldOfStudy: string18().trim().required().label("Field of Study"),
860
931
  duration: DurationSchema({
861
932
  format: "YYYY-MM",
862
933
  startLabel: "Start Date",
863
934
  endLabel: "End Date"
864
935
  }).required().label("Duration"),
865
- description: string17().trim().max(3e3).optional().label("Description"),
866
- isDistanceLearning: boolean11().default(false).label("Is Distance Learning"),
936
+ description: string18().trim().max(3e3).optional().label("Description"),
937
+ isDistanceLearning: boolean12().default(false).label("Is Distance Learning"),
867
938
  location: LocationSchema.required().label("Location"),
868
- studyType: string17().oneOf(SupportedStudyTypes).trim().max(50).required().label("Study Type")
939
+ studyType: string18().oneOf(SupportedStudyTypes).trim().max(50).required().label("Study Type")
869
940
  }).noUnknown().strict().label("Education");
870
941
 
871
942
  // src/user/user-skill.schema.ts
872
- import { object as object21, string as string18 } from "yup";
873
- var UserSkillSchema = object21({
874
- name: string18().required().label("Name"),
875
- proficiencyLevel: string18().oneOf(SupportedProficiencyLevels).required().label("Proficiency Level"),
943
+ import { object as object22, string as string19 } from "yup";
944
+ var UserSkillSchema = object22({
945
+ name: string19().required().label("Name"),
946
+ proficiencyLevel: string19().oneOf(SupportedProficiencyLevels).required().label("Proficiency Level"),
876
947
  lastUsed: dateString().format("YYYY-MM").nullable().optional().label("Last Used")
877
948
  }).noUnknown().strict().label("Skill");
878
949
 
879
950
  // src/user/project.schema.ts
880
- import { object as object22, string as string19 } from "yup";
881
- var UserProjectSchema = object22({
882
- name: string19().trim().max(50).required().label("Name"),
883
- url: string19().optional().label("URL"),
884
- description: string19().trim().min(100).max(3e3).required().label("Description"),
951
+ import { object as object23, string as string20 } from "yup";
952
+ var UserProjectSchema = object23({
953
+ name: string20().trim().max(50).required().label("Name"),
954
+ url: string20().optional().label("URL"),
955
+ description: string20().trim().min(100).max(3e3).required().label("Description"),
885
956
  duration: DurationSchema({
886
957
  format: "YYYY-MM",
887
958
  startLabel: "Start Date",
@@ -890,83 +961,83 @@ var UserProjectSchema = object22({
890
961
  }).noUnknown().strict().label("Project");
891
962
 
892
963
  // src/user/user-certification.schema.ts
893
- import { object as object23, string as string20 } from "yup";
894
- var UserCertificationSchema = object23({
895
- name: string20().trim().max(100).required().label("Name"),
964
+ import { object as object24, string as string21 } from "yup";
965
+ var UserCertificationSchema = object24({
966
+ name: string21().trim().max(100).required().label("Name"),
896
967
  // TODO: Add validation for authority
897
- authority: string20().trim().max(100).required().label("Authority"),
898
- licenseNumber: string20().trim().max(50).optional().label("License Number"),
968
+ authority: string21().trim().max(100).required().label("Authority"),
969
+ licenseNumber: string21().trim().max(50).optional().label("License Number"),
899
970
  duration: DurationSchema({
900
971
  format: "YYYY-MM",
901
972
  startLabel: "Start Date",
902
973
  endLabel: "End Date"
903
974
  }).optional().nullable().label("Duration"),
904
- url: string20().optional().label("URL")
975
+ url: string21().optional().label("URL")
905
976
  }).noUnknown().strict().label("Certification");
906
977
 
907
978
  // src/user/user-interest.schema.ts
908
- import { string as string21 } from "yup";
909
- var UserInterestSchema = string21().trim().required().label("Interest");
979
+ import { string as string22 } from "yup";
980
+ var UserInterestSchema = string22().trim().required().label("Interest");
910
981
 
911
982
  // src/user/user-language.schema.ts
912
- import { object as object24, string as string22 } from "yup";
913
- var UserLanguageSchema = object24({
914
- name: string22().trim().required().label("Name"),
915
- proficiencyLevel: string22().oneOf(SupportedProficiencyLevels).trim().max(50).required().label("Proficiency Level")
983
+ import { object as object25, string as string23 } from "yup";
984
+ var UserLanguageSchema = object25({
985
+ name: string23().trim().required().label("Name"),
986
+ proficiencyLevel: string23().oneOf(SupportedProficiencyLevels).trim().max(50).required().label("Proficiency Level")
916
987
  }).noUnknown().strict().label("Language");
917
988
 
918
989
  // src/user/user-additional-info.schema.ts
919
- import { object as object25, string as string23 } from "yup";
920
- var UserAdditionalInfoSchema = object25({
921
- title: string23().trim().max(60).required().label("Title"),
922
- description: string23().trim().max(1e3).required().label("Description")
990
+ import { object as object26, string as string24 } from "yup";
991
+ var UserAdditionalInfoSchema = object26({
992
+ title: string24().trim().max(60).required().label("Title"),
993
+ description: string24().trim().max(1e3).required().label("Description")
923
994
  }).noUnknown().strict().label("User Additional Info");
924
995
 
925
996
  // src/user/general-detail.schema.ts
926
- import { object as object26, string as string24 } from "yup";
927
- var UserGeneralDetailSchema = object26({
928
- id: string24().optional().label("ID"),
929
- name: object26().shape({
930
- first: string24().trim().max(50).required().label("First Name"),
931
- last: string24().trim().max(50).optional().label("Last Name")
997
+ import { object as object27, string as string25 } from "yup";
998
+ var UserGeneralDetailSchema = object27({
999
+ id: string25().optional().label("ID"),
1000
+ name: object27().shape({
1001
+ first: string25().trim().max(50).required().label("First Name"),
1002
+ last: string25().trim().max(50).optional().label("Last Name")
932
1003
  }).required().label("Name"),
933
- headline: string24().trim().max(200).optional().label("Headline"),
934
- image: string24().optional().label("Image"),
935
- aboutMe: string24().trim().max(3e3).optional().label("About Me"),
936
- email: string24().required().label("Email"),
937
- mobile: string24().nullable().optional().label("Mobile"),
938
- emailVerified: string24().nullable().optional().label("Email Verified"),
939
- mobileVerified: string24().nullable().optional().label("Mobile Verified"),
940
- experienceLevel: string24().oneOf(SupportedExperienceLevels).required().label("Experience level"),
1004
+ headline: string25().trim().max(200).optional().label("Headline"),
1005
+ image: string25().optional().label("Image"),
1006
+ aboutMe: string25().trim().max(3e3).optional().label("About Me"),
1007
+ email: string25().required().label("Email"),
1008
+ mobile: string25().nullable().optional().label("Mobile"),
1009
+ emailVerified: string25().nullable().optional().label("Email Verified"),
1010
+ mobileVerified: string25().nullable().optional().label("Mobile Verified"),
1011
+ experienceLevel: string25().oneOf(SupportedExperienceLevels).required().label("Experience level"),
941
1012
  location: LocationSchema.required().label("Location"),
942
- region: object26().shape({
943
- country: string24().trim().required().label("Region Country"),
944
- lang: string24().trim().required().label("Region Language")
1013
+ region: object27().shape({
1014
+ country: string25().trim().required().label("Region Country"),
1015
+ lang: string25().trim().required().label("Region Language")
945
1016
  }).optional().default(void 0).label("Region"),
946
- profileVisibility: string24().oneOf(SupportedUserProfileVisibilities).default("public" /* Public */).label("Profile Visibility")
1017
+ profileVisibility: string25().oneOf(SupportedUserProfileVisibilities).default("public" /* Public */).label("Profile Visibility")
947
1018
  }).noUnknown().strict().label("General Detail");
948
1019
 
949
1020
  // src/user/user-job-preferences.schema.ts
950
- import { array as array9, boolean as boolean12, date as date2, number as number7, object as object27, string as string25 } from "yup";
951
- var UserJobPreferencesSchema = object27({
1021
+ import { array as array10, boolean as boolean13, date as date2, number as number8, object as object28, string as string26 } from "yup";
1022
+ var UserJobPreferencesSchema = object28({
952
1023
  /**
953
1024
  * Whether the job seeker is openly signalling availability. Surfaces the
954
1025
  * "Open to work" badge on the public profile and boosts visibility in
955
1026
  * recruiter search. Defaults to `false` - users must opt in.
956
1027
  */
957
- openToWork: boolean12().default(false).label("Open to Work"),
1028
+ openToWork: boolean13().default(false).label("Open to Work"),
958
1029
  /**
959
1030
  * How urgently the user is looking for their next job. Drives match scoring
960
1031
  * and can downgrade the visibility of expired or low-relevance postings for
961
1032
  * users in `passively_browsing` mode.
962
1033
  */
963
- jobSearchUrgency: string25().oneOf(SupportedJobSearchUrgencies).required().label("Job Search Urgency"),
1034
+ jobSearchUrgency: string26().oneOf(SupportedJobSearchUrgencies).required().label("Job Search Urgency"),
964
1035
  /**
965
1036
  * Locations the user wants to work in. Stores the full `LocationSchema`
966
1037
  * shape (country, city, state, geo, etc.) as returned by the locations
967
1038
  * autocomplete API.
968
1039
  */
969
- jobLocations: array9().of(LocationSchema.required().label("Job Location")).min(1, "Pick at least one preferred location.").required().label("Job Locations"),
1040
+ jobLocations: array10().of(LocationSchema.required().label("Job Location")).min(1, "Pick at least one preferred location.").required().label("Job Locations"),
970
1041
  /**
971
1042
  * Seniority levels the user wants jobs to be matched against. Multi-select
972
1043
  * (up to 2) for borderline candidates.
@@ -977,7 +1048,7 @@ var UserJobPreferencesSchema = object27({
977
1048
  * Both reuse the same `ExperienceLevel` taxonomy from `common.constant.ts`
978
1049
  * so search/match logic doesn't have to translate between two vocabularies.
979
1050
  */
980
- targetExperienceLevels: array9().of(string25().oneOf(SupportedExperienceLevels).required()).min(1, "Pick at least one experience level.").max(2, "You can select at most 2 experience levels.").required().label("Target Experience Levels"),
1051
+ targetExperienceLevels: array10().of(string26().oneOf(SupportedExperienceLevels).required()).min(1, "Pick at least one experience level.").max(2, "You can select at most 2 experience levels.").required().label("Target Experience Levels"),
981
1052
  /**
982
1053
  * Specializations the user is searching for - free-form titles.
983
1054
  *
@@ -986,19 +1057,19 @@ var UserJobPreferencesSchema = object27({
986
1057
  * surfaces a curated "Popular Roles" picker for discovery, but the user
987
1058
  * can ultimately add any title; matching/normalization happens downstream.
988
1059
  */
989
- jobRoles: array9().of(string25().trim().max(120).required()).min(1, "Pick at least one role.").required().label("Job Roles"),
1060
+ jobRoles: array10().of(string26().trim().max(120).required()).min(1, "Pick at least one role.").required().label("Job Roles"),
990
1061
  /**
991
1062
  * Minimum acceptable annual base salary, in `minSalaryCurrency`.
992
1063
  * Stored as an integer in the currency's major unit (e.g. dollars, not cents).
993
1064
  * Defaults to 0 ("any salary") rather than being optional - every job-seeker
994
1065
  * has a floor, even if it's zero.
995
1066
  */
996
- minSalary: number7().integer().min(MIN_SALARY_LOWER_BOUND).max(MIN_SALARY_UPPER_BOUND).default(0).required().label("Minimum Salary"),
997
- minSalaryCurrency: string25().oneOf(SupportedSalaryCurrencies).default("USD").required().label("Minimum Salary Currency"),
1067
+ minSalary: number8().integer().min(MIN_SALARY_LOWER_BOUND).max(MIN_SALARY_UPPER_BOUND).default(0).required().label("Minimum Salary"),
1068
+ minSalaryCurrency: string26().oneOf(SupportedSalaryCurrencies).default("USD").required().label("Minimum Salary Currency"),
998
1069
  /**
999
1070
  * Marketing-attribution capture from the final onboarding step.
1000
1071
  */
1001
- referralSource: string25().oneOf(SupportedReferralSources).required().label("Referral Source"),
1072
+ referralSource: string26().oneOf(SupportedReferralSources).required().label("Referral Source"),
1002
1073
  /**
1003
1074
  * Resumes the user has uploaded. Embedded directly on the user document
1004
1075
  * (capped at 5) - they're cheap to denormalize, every wizard step that
@@ -1012,18 +1083,18 @@ var UserJobPreferencesSchema = object27({
1012
1083
  * AI resume-builder flow - onboarding uploads land here, AI-built resumes
1013
1084
  * live in their own collection.
1014
1085
  */
1015
- resumes: array9().of(
1016
- object27({
1086
+ resumes: array10().of(
1087
+ object28({
1017
1088
  // Stable id assigned on upload - used to address a specific entry
1018
1089
  // for delete / set-primary operations without exposing the GCS URL
1019
1090
  // as a route key.
1020
- id: string25().required().label("ID"),
1021
- url: string25().required().label("Resume URL"),
1022
- filename: string25().trim().max(255).optional().label("Filename"),
1023
- sizeBytes: number7().integer().min(0).optional().label("Size (bytes)"),
1024
- mimeType: string25().trim().max(120).optional().label("MIME Type"),
1091
+ id: string26().required().label("ID"),
1092
+ url: string26().required().label("Resume URL"),
1093
+ filename: string26().trim().max(255).optional().label("Filename"),
1094
+ sizeBytes: number8().integer().min(0).optional().label("Size (bytes)"),
1095
+ mimeType: string26().trim().max(120).optional().label("MIME Type"),
1025
1096
  uploadedAt: date2().required().label("Uploaded At"),
1026
- isPrimary: boolean12().default(false).label("Is Primary")
1097
+ isPrimary: boolean13().default(false).label("Is Primary")
1027
1098
  }).noUnknown().strict().label("Resume")
1028
1099
  ).max(5, "You can keep at most 5 resumes on file.").default([]).label("Resumes"),
1029
1100
  /**
@@ -1050,106 +1121,106 @@ var UserJobPreferencesSchema = object27({
1050
1121
  * APIs. If `user.<field>` is empty there, the correct behavior is empty,
1051
1122
  * because the user has not confirmed it yet.
1052
1123
  */
1053
- pendingPrefill: object27({
1054
- headline: string25().trim().optional().label("Pending Headline"),
1055
- aboutMe: string25().trim().optional().label("Pending About Me"),
1056
- mobile: string25().trim().optional().label("Pending Mobile"),
1124
+ pendingPrefill: object28({
1125
+ headline: string26().trim().optional().label("Pending Headline"),
1126
+ aboutMe: string26().trim().optional().label("Pending About Me"),
1127
+ mobile: string26().trim().optional().label("Pending Mobile"),
1057
1128
  location: LocationSchema.optional().default(void 0).label("Pending Location"),
1058
- experienceLevel: string25().oneOf(SupportedExperienceLevels).optional().label("Pending Experience Level"),
1059
- socialAccounts: array9().of(SocialAccountSchema.required()).optional().label("Pending Social Accounts"),
1060
- workExperiences: array9().of(WorkExperienceSchema.required()).optional().label("Pending Work Experiences"),
1061
- educations: array9().of(EducationSchema.required()).optional().label("Pending Educations")
1129
+ experienceLevel: string26().oneOf(SupportedExperienceLevels).optional().label("Pending Experience Level"),
1130
+ socialAccounts: array10().of(SocialAccountSchema.required()).optional().label("Pending Social Accounts"),
1131
+ workExperiences: array10().of(WorkExperienceSchema.required()).optional().label("Pending Work Experiences"),
1132
+ educations: array10().of(EducationSchema.required()).optional().label("Pending Educations")
1062
1133
  }).nullable().optional().default(void 0).label("Pending Prefill")
1063
1134
  }).noUnknown().strict().label("User Job Preferences Schema");
1064
1135
 
1065
1136
  // src/user/user-recruiter-profile.schema.ts
1066
- import { array as array10, object as object28, string as string26 } from "yup";
1067
- var RecruiterPageLinkSchema = object28({
1137
+ import { array as array11, object as object29, string as string27 } from "yup";
1138
+ var RecruiterPageLinkSchema = object29({
1068
1139
  page: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Company Page"),
1069
- jobTitle: string26().trim().max(100).optional().label("Job Title")
1140
+ jobTitle: string27().trim().max(100).optional().label("Job Title")
1070
1141
  }).noUnknown().strict().label("Recruiter Page Link");
1071
- var UserRecruiterProfileSchema = object28({
1072
- recruiterProfile: object28({
1073
- hiringFor: array10().of(RecruiterPageLinkSchema).default([]).label("Hiring For")
1142
+ var UserRecruiterProfileSchema = object29({
1143
+ recruiterProfile: object29({
1144
+ hiringFor: array11().of(RecruiterPageLinkSchema).default([]).label("Hiring For")
1074
1145
  }).optional().default(void 0).label("Recruiter Profile")
1075
1146
  }).noUnknown().strict().label("User Recruiter Profile");
1076
1147
 
1077
1148
  // src/user/user-coordinator-profile.schema.ts
1078
- import { array as array11, object as object29, string as string27 } from "yup";
1079
- var CoordinatorPageLinkSchema = object29({
1149
+ import { array as array12, object as object30, string as string28 } from "yup";
1150
+ var CoordinatorPageLinkSchema = object30({
1080
1151
  page: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Institute Page"),
1081
- jobTitle: string27().trim().max(100).optional().label("Job Title")
1152
+ jobTitle: string28().trim().max(100).optional().label("Job Title")
1082
1153
  }).noUnknown().strict().label("Coordinator Page Link");
1083
- var UserCoordinatorProfileSchema = object29({
1084
- coordinatorProfile: object29({
1085
- coordinatesAt: array11().of(CoordinatorPageLinkSchema).default([]).label("Coordinates At")
1154
+ var UserCoordinatorProfileSchema = object30({
1155
+ coordinatorProfile: object30({
1156
+ coordinatesAt: array12().of(CoordinatorPageLinkSchema).default([]).label("Coordinates At")
1086
1157
  }).optional().default(void 0).label("Coordinator Profile")
1087
1158
  }).noUnknown().strict().label("User Coordinator Profile");
1088
1159
 
1089
1160
  // src/user/user.schema.ts
1090
- var UserSchema = object30({
1161
+ var UserSchema = object31({
1091
1162
  /**
1092
1163
  * Related auth account id from auth Server (e.g. Better auth https://auth.thejob.dev)
1093
1164
  */
1094
- authAccountId: string28().required().label("Auth Account ID"),
1165
+ authAccountId: string29().required().label("Auth Account ID"),
1095
1166
  /**
1096
1167
  * Social media information about the user (e.g. LinkedIn, GitHub, etc.)
1097
1168
  */
1098
- socialAccounts: array12().of(SocialAccountSchema).default([]).label("Social Accounts"),
1169
+ socialAccounts: array13().of(SocialAccountSchema).default([]).label("Social Accounts"),
1099
1170
  /**
1100
1171
  * Work experience information about the user
1101
1172
  */
1102
- workExperiences: array12().of(WorkExperienceSchema).required().default([]).label("Work Experiences"),
1173
+ workExperiences: array13().of(WorkExperienceSchema).required().default([]).label("Work Experiences"),
1103
1174
  /**
1104
1175
  * Education information about the user
1105
1176
  */
1106
- educations: array12().of(EducationSchema).required().default([]).label("Educations"),
1177
+ educations: array13().of(EducationSchema).required().default([]).label("Educations"),
1107
1178
  /**
1108
1179
  * Skills information about the user
1109
1180
  */
1110
- skills: array12().of(UserSkillSchema).required().default([]).label("Skills"),
1181
+ skills: array13().of(UserSkillSchema).required().default([]).label("Skills"),
1111
1182
  /**
1112
1183
  * Projects information about the user
1113
1184
  */
1114
- projects: array12().of(UserProjectSchema).default([]).label("Projects"),
1185
+ projects: array13().of(UserProjectSchema).default([]).label("Projects"),
1115
1186
  /**
1116
1187
  * Certifications information about the user
1117
1188
  */
1118
- certifications: array12().of(UserCertificationSchema).default([]).label("Certifications"),
1189
+ certifications: array13().of(UserCertificationSchema).default([]).label("Certifications"),
1119
1190
  /**
1120
1191
  * Interests information about the user.
1121
1192
  */
1122
- interests: array12().of(UserInterestSchema).optional().default([]).label("Interests"),
1193
+ interests: array13().of(UserInterestSchema).optional().default([]).label("Interests"),
1123
1194
  /**
1124
1195
  * Languages information about the user
1125
1196
  */
1126
- languages: array12().of(UserLanguageSchema).required().default([]).label("Languages"),
1197
+ languages: array13().of(UserLanguageSchema).required().default([]).label("Languages"),
1127
1198
  /**
1128
1199
  * Additional information about the user
1129
1200
  */
1130
- additionalInfo: array12().of(UserAdditionalInfoSchema).default([]).label("Additional Information"),
1201
+ additionalInfo: array13().of(UserAdditionalInfoSchema).default([]).label("Additional Information"),
1131
1202
  /**
1132
1203
  * Status of the user account
1133
1204
  */
1134
- status: string28().oneOf(SupportedUserStatuses).required().label("Status"),
1205
+ status: string29().oneOf(SupportedUserStatuses).required().label("Status"),
1135
1206
  /**
1136
1207
  * Roles assigned to the user
1137
1208
  */
1138
- roles: array12().of(string28().oneOf(SupportedUserRoles)).required().default(DefaultUserRoles).label("Roles"),
1209
+ roles: array13().of(string29().oneOf(SupportedUserRoles)).required().default(DefaultUserRoles).label("Roles"),
1139
1210
  /**
1140
1211
  * Vector embedding of the user's profile for semantic/hybrid search.
1141
1212
  * Generated from a structured summary of skills, experience, education, etc.
1142
1213
  * Only generated for public profiles with sufficient completeness (≥60%).
1143
1214
  */
1144
- embedding: object30({
1145
- vector: array12(number8().required()).optional().label("Embedding vector"),
1146
- model: string28().optional().label("Embedding model")
1215
+ embedding: object31({
1216
+ vector: array13(number9().required()).optional().label("Embedding vector"),
1217
+ model: string29().optional().label("Embedding model")
1147
1218
  }).nullable().optional().label("Embedding")
1148
1219
  }).concat(UserGeneralDetailSchema).concat(UserJobPreferencesSchema).concat(UserRecruiterProfileSchema).concat(UserCoordinatorProfileSchema).noUnknown().strict().label("User Schema");
1149
1220
 
1150
1221
  // src/user/user-completeness.schema.ts
1151
- import { object as object31 } from "yup";
1152
- var UserCompletenessSchema = object31({
1222
+ import { object as object32 } from "yup";
1223
+ var UserCompletenessSchema = object32({
1153
1224
  additionalInfo: CompletenessScoreSchema(0).label("Additional Info"),
1154
1225
  // Optional
1155
1226
  certifications: CompletenessScoreSchema(0).label("Certifications"),
@@ -1212,6 +1283,8 @@ export {
1212
1283
  PageStatus,
1213
1284
  PageType,
1214
1285
  PaginationSchema,
1286
+ PostSchema,
1287
+ PostStatus,
1215
1288
  ProficiencyLevel,
1216
1289
  QuestionSchema,
1217
1290
  QuestionType,
@@ -1240,6 +1313,7 @@ export {
1240
1313
  SupportedJobStatuses,
1241
1314
  SupportedPageStatuses,
1242
1315
  SupportedPageTypes,
1316
+ SupportedPostStatuses,
1243
1317
  SupportedProficiencyLevels,
1244
1318
  SupportedQuestionTypes,
1245
1319
  SupportedReferralSources,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thejob/schema",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
package/src/index.ts CHANGED
@@ -26,6 +26,12 @@ export * from "./group/group.constant.js";
26
26
  export * from "./job/job.schema.js";
27
27
  export * from "./job/job.constant.js";
28
28
 
29
+ /**
30
+ * Post schemas and constants (blog).
31
+ */
32
+ export * from "./post/post.schema.js";
33
+ export * from "./post/post.constant.js";
34
+
29
35
  /**
30
36
  * Question schemas and constants (job questionnaire).
31
37
  */
@@ -131,6 +131,14 @@ export const JobSchema = object({
131
131
  .nullable()
132
132
  .label("Last expiry check at"),
133
133
 
134
+ /**
135
+ * When this job transitioned to `status: closed` (epoch ms). Server-owned
136
+ * operational field: stamped by every jobs-service close path (expiry
137
+ * auto-close and the dated lapse sweep). Distinct from `updatedAt` so a later
138
+ * edit to a closed job does not move it.
139
+ */
140
+ closedAt: number().optional().nullable().label("Closed at"),
141
+
134
142
  experienceLevel: string()
135
143
  .oneOf(SupportedExperienceLevels)
136
144
  .nullable()
@@ -188,6 +196,24 @@ export const JobSchema = object({
188
196
 
189
197
  jdSummary: string().optional().label("JD Summary"),
190
198
 
199
+ /**
200
+ * Structured, model-extracted sections of the posting, re-organized into clean
201
+ * bullet points. This is our OWN unique presentation of the job (rendered as the
202
+ * primary, server-side page content) so the page adds value over the raw scraped
203
+ * description and is not penalized by Google as duplicate content. All sections
204
+ * optional: absent on jobs not yet (re-)enriched, and any section is `[]`/omitted
205
+ * when the posting genuinely has none.
206
+ */
207
+ jobHighlights: object({
208
+ responsibilities: array().of(string()).optional().label("Responsibilities"),
209
+ requirements: array().of(string()).optional().label("Requirements"),
210
+ benefits: array().of(string()).optional().label("Benefits"),
211
+ qualifications: array().of(string()).optional().label("Qualifications"),
212
+ })
213
+ .nullable()
214
+ .optional()
215
+ .label("Job Highlights"),
216
+
191
217
  canCreateAlert: boolean().optional().label("Can create alert"),
192
218
 
193
219
  canDirectApply: boolean().optional().label("Can direct apply"),
@@ -0,0 +1,7 @@
1
+ export enum PostStatus {
2
+ Draft = "draft",
3
+ Published = "published",
4
+ Archived = "archived",
5
+ }
6
+
7
+ export const SupportedPostStatuses = Object.values(PostStatus);