@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.cjs +205 -128
- package/dist/index.d.cts +70 -1
- package/dist/index.d.ts +70 -1
- package/dist/index.js +198 -124
- package/package.json +1 -1
- package/src/index.ts +6 -0
- package/src/job/job.schema.ts +26 -0
- package/src/post/post.constant.ts +7 -0
- package/src/post/post.schema.ts +68 -0
- package/.claude/scheduled_tasks.lock +0 -1
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
|
|
704
|
-
var PaginationSchema =
|
|
705
|
-
page:
|
|
706
|
-
limit:
|
|
707
|
-
sortBy:
|
|
708
|
-
nextPage:
|
|
709
|
-
prevPage:
|
|
710
|
-
sortDirection:
|
|
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
|
|
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 =
|
|
821
|
+
var ReportSchema = object19({
|
|
751
822
|
type: mixed4().oneOf(SupportedResourceTypes).required().label("Type"),
|
|
752
|
-
resourceId:
|
|
823
|
+
resourceId: string16().required().label("Resource ID"),
|
|
753
824
|
reason: mixed4().oneOf(SupportedReportReasons).required("Please choose a reason").label("Reason"),
|
|
754
|
-
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
|
|
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
|
|
841
|
-
var WorkExperienceSchema =
|
|
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:
|
|
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:
|
|
920
|
+
description: string17().trim().max(3e3).optional().label("Description"),
|
|
850
921
|
location: LocationSchema.required().label("Location"),
|
|
851
|
-
isRemote:
|
|
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
|
|
856
|
-
var EducationSchema =
|
|
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:
|
|
859
|
-
fieldOfStudy:
|
|
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:
|
|
866
|
-
isDistanceLearning:
|
|
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:
|
|
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
|
|
873
|
-
var UserSkillSchema =
|
|
874
|
-
name:
|
|
875
|
-
proficiencyLevel:
|
|
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
|
|
881
|
-
var UserProjectSchema =
|
|
882
|
-
name:
|
|
883
|
-
url:
|
|
884
|
-
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
|
|
894
|
-
var UserCertificationSchema =
|
|
895
|
-
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:
|
|
898
|
-
licenseNumber:
|
|
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:
|
|
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
|
|
909
|
-
var UserInterestSchema =
|
|
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
|
|
913
|
-
var UserLanguageSchema =
|
|
914
|
-
name:
|
|
915
|
-
proficiencyLevel:
|
|
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
|
|
920
|
-
var UserAdditionalInfoSchema =
|
|
921
|
-
title:
|
|
922
|
-
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
|
|
927
|
-
var UserGeneralDetailSchema =
|
|
928
|
-
id:
|
|
929
|
-
name:
|
|
930
|
-
first:
|
|
931
|
-
last:
|
|
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:
|
|
934
|
-
image:
|
|
935
|
-
aboutMe:
|
|
936
|
-
email:
|
|
937
|
-
mobile:
|
|
938
|
-
emailVerified:
|
|
939
|
-
mobileVerified:
|
|
940
|
-
experienceLevel:
|
|
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:
|
|
943
|
-
country:
|
|
944
|
-
lang:
|
|
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:
|
|
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
|
|
951
|
-
var UserJobPreferencesSchema =
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
997
|
-
minSalaryCurrency:
|
|
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:
|
|
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:
|
|
1016
|
-
|
|
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:
|
|
1021
|
-
url:
|
|
1022
|
-
filename:
|
|
1023
|
-
sizeBytes:
|
|
1024
|
-
mimeType:
|
|
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:
|
|
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:
|
|
1054
|
-
headline:
|
|
1055
|
-
aboutMe:
|
|
1056
|
-
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:
|
|
1059
|
-
socialAccounts:
|
|
1060
|
-
workExperiences:
|
|
1061
|
-
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
|
|
1067
|
-
var RecruiterPageLinkSchema =
|
|
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:
|
|
1140
|
+
jobTitle: string27().trim().max(100).optional().label("Job Title")
|
|
1070
1141
|
}).noUnknown().strict().label("Recruiter Page Link");
|
|
1071
|
-
var UserRecruiterProfileSchema =
|
|
1072
|
-
recruiterProfile:
|
|
1073
|
-
hiringFor:
|
|
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
|
|
1079
|
-
var CoordinatorPageLinkSchema =
|
|
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:
|
|
1152
|
+
jobTitle: string28().trim().max(100).optional().label("Job Title")
|
|
1082
1153
|
}).noUnknown().strict().label("Coordinator Page Link");
|
|
1083
|
-
var UserCoordinatorProfileSchema =
|
|
1084
|
-
coordinatorProfile:
|
|
1085
|
-
coordinatesAt:
|
|
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 =
|
|
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:
|
|
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:
|
|
1169
|
+
socialAccounts: array13().of(SocialAccountSchema).default([]).label("Social Accounts"),
|
|
1099
1170
|
/**
|
|
1100
1171
|
* Work experience information about the user
|
|
1101
1172
|
*/
|
|
1102
|
-
workExperiences:
|
|
1173
|
+
workExperiences: array13().of(WorkExperienceSchema).required().default([]).label("Work Experiences"),
|
|
1103
1174
|
/**
|
|
1104
1175
|
* Education information about the user
|
|
1105
1176
|
*/
|
|
1106
|
-
educations:
|
|
1177
|
+
educations: array13().of(EducationSchema).required().default([]).label("Educations"),
|
|
1107
1178
|
/**
|
|
1108
1179
|
* Skills information about the user
|
|
1109
1180
|
*/
|
|
1110
|
-
skills:
|
|
1181
|
+
skills: array13().of(UserSkillSchema).required().default([]).label("Skills"),
|
|
1111
1182
|
/**
|
|
1112
1183
|
* Projects information about the user
|
|
1113
1184
|
*/
|
|
1114
|
-
projects:
|
|
1185
|
+
projects: array13().of(UserProjectSchema).default([]).label("Projects"),
|
|
1115
1186
|
/**
|
|
1116
1187
|
* Certifications information about the user
|
|
1117
1188
|
*/
|
|
1118
|
-
certifications:
|
|
1189
|
+
certifications: array13().of(UserCertificationSchema).default([]).label("Certifications"),
|
|
1119
1190
|
/**
|
|
1120
1191
|
* Interests information about the user.
|
|
1121
1192
|
*/
|
|
1122
|
-
interests:
|
|
1193
|
+
interests: array13().of(UserInterestSchema).optional().default([]).label("Interests"),
|
|
1123
1194
|
/**
|
|
1124
1195
|
* Languages information about the user
|
|
1125
1196
|
*/
|
|
1126
|
-
languages:
|
|
1197
|
+
languages: array13().of(UserLanguageSchema).required().default([]).label("Languages"),
|
|
1127
1198
|
/**
|
|
1128
1199
|
* Additional information about the user
|
|
1129
1200
|
*/
|
|
1130
|
-
additionalInfo:
|
|
1201
|
+
additionalInfo: array13().of(UserAdditionalInfoSchema).default([]).label("Additional Information"),
|
|
1131
1202
|
/**
|
|
1132
1203
|
* Status of the user account
|
|
1133
1204
|
*/
|
|
1134
|
-
status:
|
|
1205
|
+
status: string29().oneOf(SupportedUserStatuses).required().label("Status"),
|
|
1135
1206
|
/**
|
|
1136
1207
|
* Roles assigned to the user
|
|
1137
1208
|
*/
|
|
1138
|
-
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:
|
|
1145
|
-
vector:
|
|
1146
|
-
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
|
|
1152
|
-
var UserCompletenessSchema =
|
|
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
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
|
*/
|
package/src/job/job.schema.ts
CHANGED
|
@@ -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"),
|