@thejob/schema 2.1.4 → 2.1.6
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/CLAUDE.md +25 -2
- package/dist/index.cjs +62 -5
- package/dist/index.d.cts +35 -7
- package/dist/index.d.ts +35 -7
- package/dist/index.js +67 -12
- package/package.json +1 -1
- package/src/job/job.schema.ts +11 -1
- package/src/skill/skill.schema.ts +23 -0
- package/src/user/general-detail.schema.ts +27 -5
- package/src/user/user.constant.ts +20 -0
package/CLAUDE.md
CHANGED
|
@@ -50,8 +50,31 @@ Practical consequences:
|
|
|
50
50
|
|
|
51
51
|
Enum string values are consumed as literals across services and stored in Mongo, so
|
|
52
52
|
their casing is data, not style. Group `managedBy` / `visibility` / `status` are
|
|
53
|
-
**lowercase** (`system`, `private`, `archived`)
|
|
54
|
-
|
|
53
|
+
**lowercase** (`system`, `private`, `archived`), as are `SavedSearchStatus` and
|
|
54
|
+
`JobAlertFrequency`. Changing a case is a data migration, not a refactor —
|
|
55
|
+
existing documents will silently stop matching.
|
|
56
|
+
|
|
57
|
+
## The consent fields are a compliance surface
|
|
58
|
+
|
|
59
|
+
Three shapes here decide whether the platform may mail someone, and each behaves
|
|
60
|
+
differently on purpose:
|
|
61
|
+
|
|
62
|
+
- **`notificationPrefs`** (`user.schema.ts`) — per-channel, opt-**out**. The
|
|
63
|
+
consumer reads `!== false`, so a user whose document predates the field must
|
|
64
|
+
read as *enabled*. That is why adding it needed no backfill migration, and why
|
|
65
|
+
a "tidy-up" that made the fields required would silently mute every legacy user.
|
|
66
|
+
- **`marketingConsent.subscribed`** — opt-**in**, read as `=== true`. The exact
|
|
67
|
+
inverse. Do not collapse the two into one shape; they have different legal
|
|
68
|
+
footing, which email-service's footer rules already encode.
|
|
69
|
+
- **`marketingConsent.jobAlertFrequency`** — a *second* gate on job-alert mail
|
|
70
|
+
only, so a user can stop alerts while staying subscribed generally.
|
|
71
|
+
|
|
72
|
+
`NotificationChannel` values are duplicated as a string union in `@thejob/notify`
|
|
73
|
+
(so that package stays dependency-free). This package remains the source of
|
|
74
|
+
truth; adding a channel means editing the enum, the `notificationPrefs` object
|
|
75
|
+
keys, its `.default()`, and that union.
|
|
76
|
+
|
|
77
|
+
Full picture: `thejob-notification-service/NOTIFICATIONS.md`.
|
|
55
78
|
|
|
56
79
|
## Conventions
|
|
57
80
|
|
package/dist/index.cjs
CHANGED
|
@@ -102,6 +102,7 @@ __export(index_exports, {
|
|
|
102
102
|
SYSTEM_DATE_FORMAT: () => SYSTEM_DATE_FORMAT,
|
|
103
103
|
SavedSearchSchema: () => SavedSearchSchema,
|
|
104
104
|
SavedSearchStatus: () => SavedSearchStatus,
|
|
105
|
+
SignupContextSource: () => SignupContextSource,
|
|
105
106
|
SkillSchema: () => SkillSchema,
|
|
106
107
|
SocialAccount: () => SocialAccount,
|
|
107
108
|
SocialAccountSchema: () => SocialAccountSchema,
|
|
@@ -135,6 +136,7 @@ __export(index_exports, {
|
|
|
135
136
|
SupportedResourceTypes: () => SupportedResourceTypes,
|
|
136
137
|
SupportedSalaryCurrencies: () => SupportedSalaryCurrencies,
|
|
137
138
|
SupportedSavedSearchStatuses: () => SupportedSavedSearchStatuses,
|
|
139
|
+
SupportedSignupContextSources: () => SupportedSignupContextSources,
|
|
138
140
|
SupportedSocialAccounts: () => SupportedSocialAccounts,
|
|
139
141
|
SupportedStudyTypes: () => SupportedStudyTypes,
|
|
140
142
|
SupportedUserProfileVisibilities: () => SupportedUserProfileVisibilities,
|
|
@@ -592,6 +594,26 @@ var PageSchema = (0, import_yup12.object)().shape({
|
|
|
592
594
|
var import_yup13 = require("yup");
|
|
593
595
|
var SkillSchema = (0, import_yup13.object)({
|
|
594
596
|
name: (0, import_yup13.string)().trim().required().label("Skill Name"),
|
|
597
|
+
/**
|
|
598
|
+
* The taxonomy concept this skill resolved to, assigned by thejob-taxonomy-service
|
|
599
|
+
* via `POST /normalize/batch`. Stable slug, e.g. "power-bi".
|
|
600
|
+
*
|
|
601
|
+
* Normalization is ADDITIVE and never authoritative over `name`: the raw string a
|
|
602
|
+
* job was posted with is always preserved, and these two fields are written
|
|
603
|
+
* alongside it. That is what makes a wrong match, a taxonomy re-run, or a service
|
|
604
|
+
* outage recoverable rather than a data loss.
|
|
605
|
+
*
|
|
606
|
+
* Optional and nullable because they genuinely are absent in two normal cases,
|
|
607
|
+
* neither of which is an error:
|
|
608
|
+
* - the term did not resolve (~22% of mentions are a long tail the taxonomy has
|
|
609
|
+
* no concept for yet), and the raw `name` stands alone;
|
|
610
|
+
* - the document predates normalization and has not been re-processed.
|
|
611
|
+
*
|
|
612
|
+
* Consumers must therefore read `conceptName ?? name`, never `conceptName` alone.
|
|
613
|
+
*/
|
|
614
|
+
conceptId: (0, import_yup13.string)().optional().nullable().label("Concept ID"),
|
|
615
|
+
/** Canonical display name for `conceptId`, e.g. "Power BI". See `conceptId`. */
|
|
616
|
+
conceptName: (0, import_yup13.string)().optional().nullable().label("Concept Name"),
|
|
595
617
|
logo: (0, import_yup13.object)({
|
|
596
618
|
light: (0, import_yup13.string)().required().label("Light Logo"),
|
|
597
619
|
dark: (0, import_yup13.string)().optional().nullable().label("Dark Logo")
|
|
@@ -797,7 +819,15 @@ var JobSchema = (0, import_yup18.object)({
|
|
|
797
819
|
designation: (0, import_yup18.string)().optional().nullable().label("Designation"),
|
|
798
820
|
employmentType: (0, import_yup18.string)().required().label("Employment type"),
|
|
799
821
|
workMode: (0, import_yup18.string)().required().label("Work mode"),
|
|
800
|
-
|
|
822
|
+
/**
|
|
823
|
+
* `conceptId`/`conceptName` carry the taxonomy-service normalization. They are
|
|
824
|
+
* picked here (not just `name`/`logo`) because SkillSchema is `.noUnknown()`:
|
|
825
|
+
* an unpicked field that is actually PRESENT fails validation, so omitting them
|
|
826
|
+
* would reject every normalized job.
|
|
827
|
+
*/
|
|
828
|
+
skills: (0, import_yup18.array)().of(
|
|
829
|
+
SkillSchema.pick(["name", "logo", "conceptId", "conceptName"]).required().label("Skill")
|
|
830
|
+
).nullable().optional().label("Skills"),
|
|
801
831
|
bookmarks: (0, import_yup18.array)().of(UserIdAndCreatedAtSchema).optional().label("Bookmarks"),
|
|
802
832
|
embedding: (0, import_yup18.object)({
|
|
803
833
|
vector: (0, import_yup18.array)((0, import_yup18.number)().required()).optional().label("Embedding vector"),
|
|
@@ -2110,6 +2140,13 @@ var UserProfileVisibility = /* @__PURE__ */ ((UserProfileVisibility2) => {
|
|
|
2110
2140
|
var SupportedUserProfileVisibilities = Object.values(
|
|
2111
2141
|
UserProfileVisibility
|
|
2112
2142
|
);
|
|
2143
|
+
var SignupContextSource = /* @__PURE__ */ ((SignupContextSource2) => {
|
|
2144
|
+
SignupContextSource2["CfHeader"] = "cf-header";
|
|
2145
|
+
SignupContextSource2["Location"] = "location";
|
|
2146
|
+
SignupContextSource2["Default"] = "default";
|
|
2147
|
+
return SignupContextSource2;
|
|
2148
|
+
})(SignupContextSource || {});
|
|
2149
|
+
var SupportedSignupContextSources = Object.values(SignupContextSource);
|
|
2113
2150
|
var UserDetailType = /* @__PURE__ */ ((UserDetailType2) => {
|
|
2114
2151
|
UserDetailType2["Overview"] = "overview";
|
|
2115
2152
|
UserDetailType2["AdditionalInfo"] = "additionalInfo";
|
|
@@ -2533,10 +2570,28 @@ var UserGeneralDetailSchema = (0, import_yup33.object)({
|
|
|
2533
2570
|
mobileVerified: (0, import_yup33.string)().nullable().optional().label("Mobile Verified"),
|
|
2534
2571
|
experienceLevel: (0, import_yup33.string)().oneOf(SupportedExperienceLevels).required().label("Experience level"),
|
|
2535
2572
|
location: LocationSchema.required().label("Location"),
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2573
|
+
/**
|
|
2574
|
+
* Where the account was created, captured once and never rewritten.
|
|
2575
|
+
*
|
|
2576
|
+
* Server-owned: listed in user-service's SERVER_CONTROLLED_FIELDS and absent
|
|
2577
|
+
* from every user-writable schema, so a client cannot set or amend it. That
|
|
2578
|
+
* immutability is the point — this answers "which market/jurisdiction governed
|
|
2579
|
+
* this user at sign-up?", which a field the user can PATCH could never do.
|
|
2580
|
+
*
|
|
2581
|
+
* `source` records HOW the value was obtained, because most of it is inferred
|
|
2582
|
+
* rather than measured, and an audit has to be able to tell those apart:
|
|
2583
|
+
* - `cf-header` measured from Cloudflare's `cf-ipcountry` at sign-up
|
|
2584
|
+
* - `location` derived from the user's self-entered profile location
|
|
2585
|
+
* - `default` assumed (no signal available); presumption, not evidence
|
|
2586
|
+
*/
|
|
2587
|
+
signupContext: (0, import_yup33.object)().shape({
|
|
2588
|
+
// ISO 3166-1 alpha-2, uppercase — the shape `cf-ipcountry` returns, so
|
|
2589
|
+
// measured and inferred values are directly comparable.
|
|
2590
|
+
country: (0, import_yup33.string)().trim().uppercase().length(2).required().label("Signup Country"),
|
|
2591
|
+
locale: (0, import_yup33.string)().trim().required().label("Signup Locale"),
|
|
2592
|
+
source: (0, import_yup33.string)().oneOf(SupportedSignupContextSources).required().label("Signup Context Source"),
|
|
2593
|
+
at: (0, import_yup33.number)().required().label("Signup Context At")
|
|
2594
|
+
}).optional().default(void 0).label("Signup Context"),
|
|
2540
2595
|
profileVisibility: (0, import_yup33.string)().oneOf(SupportedUserProfileVisibilities).default("public" /* Public */).label("Profile Visibility")
|
|
2541
2596
|
}).noUnknown().strict().label("General Detail");
|
|
2542
2597
|
|
|
@@ -2886,6 +2941,7 @@ var StudentCompletenessSchema = UserCompletenessSchema.omit([
|
|
|
2886
2941
|
SYSTEM_DATE_FORMAT,
|
|
2887
2942
|
SavedSearchSchema,
|
|
2888
2943
|
SavedSearchStatus,
|
|
2944
|
+
SignupContextSource,
|
|
2889
2945
|
SkillSchema,
|
|
2890
2946
|
SocialAccount,
|
|
2891
2947
|
SocialAccountSchema,
|
|
@@ -2919,6 +2975,7 @@ var StudentCompletenessSchema = UserCompletenessSchema.omit([
|
|
|
2919
2975
|
SupportedResourceTypes,
|
|
2920
2976
|
SupportedSalaryCurrencies,
|
|
2921
2977
|
SupportedSavedSearchStatuses,
|
|
2978
|
+
SupportedSignupContextSources,
|
|
2922
2979
|
SupportedSocialAccounts,
|
|
2923
2980
|
SupportedStudyTypes,
|
|
2924
2981
|
SupportedUserProfileVisibilities,
|
package/dist/index.d.cts
CHANGED
|
@@ -437,6 +437,8 @@ declare const JobSchema: ObjectSchema<{
|
|
|
437
437
|
employmentType: string;
|
|
438
438
|
workMode: string;
|
|
439
439
|
skills: {
|
|
440
|
+
conceptId?: string | null | undefined;
|
|
441
|
+
conceptName?: string | null | undefined;
|
|
440
442
|
name: string;
|
|
441
443
|
logo: {
|
|
442
444
|
dark?: string | null | undefined;
|
|
@@ -1500,6 +1502,24 @@ declare enum UserProfileVisibility {
|
|
|
1500
1502
|
Hidden = "hidden"
|
|
1501
1503
|
}
|
|
1502
1504
|
declare const SupportedUserProfileVisibilities: UserProfileVisibility[];
|
|
1505
|
+
/**
|
|
1506
|
+
* How a user's `signupContext` was obtained. Stored on the document, so these
|
|
1507
|
+
* strings are data: lowercase, and a rename is a migration.
|
|
1508
|
+
*
|
|
1509
|
+
* The distinction is deliberate. Only `CfHeader` is measured; the other two are
|
|
1510
|
+
* inferred, and `Default` is a bare presumption with no signal behind it at all.
|
|
1511
|
+
* Collapsing them would make an assumed country indistinguishable from an
|
|
1512
|
+
* observed one, which is precisely what a compliance record must not do.
|
|
1513
|
+
*/
|
|
1514
|
+
declare enum SignupContextSource {
|
|
1515
|
+
/** Measured from Cloudflare's `cf-ipcountry` header at sign-up. */
|
|
1516
|
+
CfHeader = "cf-header",
|
|
1517
|
+
/** Derived from the user's self-entered profile location (backfill). */
|
|
1518
|
+
Location = "location",
|
|
1519
|
+
/** Assumed; no signal was available. A presumption, not evidence. */
|
|
1520
|
+
Default = "default"
|
|
1521
|
+
}
|
|
1522
|
+
declare const SupportedSignupContextSources: SignupContextSource[];
|
|
1503
1523
|
declare enum UserDetailType {
|
|
1504
1524
|
Overview = "overview",
|
|
1505
1525
|
AdditionalInfo = "additionalInfo",
|
|
@@ -1718,6 +1738,8 @@ declare const FeedbackSchema: yup.ObjectSchema<{
|
|
|
1718
1738
|
|
|
1719
1739
|
declare const SkillSchema: yup.ObjectSchema<{
|
|
1720
1740
|
name: string;
|
|
1741
|
+
conceptId: string | null | undefined;
|
|
1742
|
+
conceptName: string | null | undefined;
|
|
1721
1743
|
logo: {
|
|
1722
1744
|
dark?: string | null | undefined;
|
|
1723
1745
|
light: string;
|
|
@@ -1731,6 +1753,8 @@ declare const SkillSchema: yup.ObjectSchema<{
|
|
|
1731
1753
|
updatedAt: number | undefined;
|
|
1732
1754
|
}, yup.AnyObject, {
|
|
1733
1755
|
name: undefined;
|
|
1756
|
+
conceptId: undefined;
|
|
1757
|
+
conceptName: undefined;
|
|
1734
1758
|
logo: null;
|
|
1735
1759
|
tags: "";
|
|
1736
1760
|
shortId: undefined;
|
|
@@ -1932,9 +1956,11 @@ declare const UserSchema: yup.ObjectSchema<{
|
|
|
1932
1956
|
coordinates: number[];
|
|
1933
1957
|
};
|
|
1934
1958
|
};
|
|
1935
|
-
|
|
1959
|
+
signupContext: {
|
|
1936
1960
|
country: string;
|
|
1937
|
-
|
|
1961
|
+
at: number;
|
|
1962
|
+
locale: string;
|
|
1963
|
+
source: NonNullable<SignupContextSource | undefined>;
|
|
1938
1964
|
} | undefined;
|
|
1939
1965
|
profileVisibility: UserProfileVisibility;
|
|
1940
1966
|
openToWork: boolean;
|
|
@@ -2137,7 +2163,7 @@ declare const UserSchema: yup.ObjectSchema<{
|
|
|
2137
2163
|
coordinates: "";
|
|
2138
2164
|
};
|
|
2139
2165
|
};
|
|
2140
|
-
|
|
2166
|
+
signupContext: undefined;
|
|
2141
2167
|
profileVisibility: UserProfileVisibility.Public;
|
|
2142
2168
|
openToWork: false;
|
|
2143
2169
|
jobSearchUrgency: undefined;
|
|
@@ -2189,9 +2215,11 @@ declare const UserGeneralDetailSchema: yup.ObjectSchema<{
|
|
|
2189
2215
|
coordinates: number[];
|
|
2190
2216
|
};
|
|
2191
2217
|
};
|
|
2192
|
-
|
|
2218
|
+
signupContext: {
|
|
2193
2219
|
country: string;
|
|
2194
|
-
|
|
2220
|
+
at: number;
|
|
2221
|
+
locale: string;
|
|
2222
|
+
source: NonNullable<SignupContextSource | undefined>;
|
|
2195
2223
|
} | undefined;
|
|
2196
2224
|
profileVisibility: UserProfileVisibility;
|
|
2197
2225
|
}, yup.AnyObject, {
|
|
@@ -2221,7 +2249,7 @@ declare const UserGeneralDetailSchema: yup.ObjectSchema<{
|
|
|
2221
2249
|
coordinates: "";
|
|
2222
2250
|
};
|
|
2223
2251
|
};
|
|
2224
|
-
|
|
2252
|
+
signupContext: undefined;
|
|
2225
2253
|
profileVisibility: UserProfileVisibility.Public;
|
|
2226
2254
|
}, "">;
|
|
2227
2255
|
|
|
@@ -2995,4 +3023,4 @@ declare const StudentCompletenessSchema: yup.ObjectSchema<{
|
|
|
2995
3023
|
};
|
|
2996
3024
|
}, "">;
|
|
2997
3025
|
|
|
2998
|
-
export { AnswerChoiceType, ApplicationReceivePreference, CampaignSchema, CampaignStatus, ChoiceQuestionSchema, Common, CompensationType, CompletenessScoreSchema, ContactTypes, CoordinatorPageLinkSchema, DISPLAY_DATE_FORMAT, DISPLAY_DATE_FORMAT_SHORT, DateStringSchema, DbDefaultSchema, DefaultPaginatedResponse, DefaultPaginationOptions, DefaultUserRoles, DurationSchema, EMPTY_STRING, EducationLevel, EducationSchema, EmployeeCount, EmploymentType, ExperienceLevel, FEEDBACK_MESSAGE_MAX, FeedbackSchema, FeedbackStatus, FeedbackType, GeneraDetailFields, GroupManagedBy, GroupMembershipSchema, GroupMembershipStatus, GroupSchema, GroupStatus, GroupTranslationSchema, GroupVisibility, InputQuestionSchema, JOB_TAXONOMY, JobAlertFrequency, JobCategory, JobIndustry, JobSchema, JobSearchUrgency, JobStatus, JobSubCategory, ListFilterSchema, LocationSchema, MIN_SALARY_LOWER_BOUND, MIN_SALARY_UPPER_BOUND, MailingListSchema, NotificationChannel, PREFILL_MIRRORED_KEYS, PRIVATE_PROFILE_FIELDS, PageSchema, PageStatus, PageType, type PaginatedResponse, PaginationSchema, PostSchema, PostStatus, type PrefillMirroredKey, type PrivateProfileField, ProficiencyLevel, QuestionSchema, QuestionType, ReadAndAcknowledgeQuestionSchema, RecruiterPageLinkSchema, ReferralSource, ReportReason, ReportSchema, ResourceType, SITEMAP_FORMAT, SYSTEM_DATE_FORMAT, SavedSearchSchema, SavedSearchStatus, SkillSchema, SocialAccount, SocialAccountSchema, StudentCompletenessSchema, StudyType, SupportedAnswerChoiceTypes, SupportedApplicationReceivePreferences, SupportedCampaignStatuses, SupportedCompensationTypes, SupportedContactTypes, SupportedEducationLevels, SupportedEmployeeCounts, SupportedEmploymentTypes, SupportedExperienceLevels, SupportedFeedbackStatuses, SupportedFeedbackTypes, SupportedJobAlertFrequencies, SupportedJobCategories, SupportedJobIndustries, SupportedJobSearchUrgencies, SupportedJobStatuses, SupportedJobSubCategories, SupportedNotificationChannels, SupportedPageStatuses, SupportedPageTypes, SupportedPostStatuses, SupportedProficiencyLevels, SupportedQuestionTypes, SupportedReferralSources, SupportedReportReasons, SupportedResourceTypes, SupportedSalaryCurrencies, type SupportedSalaryCurrency, SupportedSavedSearchStatuses, SupportedSocialAccounts, SupportedStudyTypes, SupportedUserProfileVisibilities, SupportedUserRoles, SupportedUserStatuses, SupportedWorkModes, TAXONOMY_LABELS, type TChoiceQuestionSchema, type TDurationSchema, type TInputQuestionSchema, type TJobSchema, type TPostSchema, type TQuestionSchema, type TReadAndAcknowledgeQuestionSchema, type TUserIdAndCreatedAtSchema, TermsAcceptedSchema, UserAdditionalInfoSchema, UserCertificationSchema, UserCompletenessSchema, UserCoordinatorProfileSchema, UserDetailType, UserGeneralDetailSchema, UserIdAndCreatedAtSchema, UserInterestSchema, UserJobPreferencesSchema, UserLanguageSchema, type UserProfileOverview, UserProfileVisibility, UserProjectSchema, UserRecruiterProfileSchema, UserRole, UserSchema, UserSkillSchema, UserStatus, WorkExperienceSchema, WorkMode, categoryOfIndustry, categoryOfSubCategory, dateString, deriveIndustry, getSchemaByQuestion, industriesOfCategory, industryOfSubCategory, subCategoriesOfIndustry };
|
|
3026
|
+
export { AnswerChoiceType, ApplicationReceivePreference, CampaignSchema, CampaignStatus, ChoiceQuestionSchema, Common, CompensationType, CompletenessScoreSchema, ContactTypes, CoordinatorPageLinkSchema, DISPLAY_DATE_FORMAT, DISPLAY_DATE_FORMAT_SHORT, DateStringSchema, DbDefaultSchema, DefaultPaginatedResponse, DefaultPaginationOptions, DefaultUserRoles, DurationSchema, EMPTY_STRING, EducationLevel, EducationSchema, EmployeeCount, EmploymentType, ExperienceLevel, FEEDBACK_MESSAGE_MAX, FeedbackSchema, FeedbackStatus, FeedbackType, GeneraDetailFields, GroupManagedBy, GroupMembershipSchema, GroupMembershipStatus, GroupSchema, GroupStatus, GroupTranslationSchema, GroupVisibility, InputQuestionSchema, JOB_TAXONOMY, JobAlertFrequency, JobCategory, JobIndustry, JobSchema, JobSearchUrgency, JobStatus, JobSubCategory, ListFilterSchema, LocationSchema, MIN_SALARY_LOWER_BOUND, MIN_SALARY_UPPER_BOUND, MailingListSchema, NotificationChannel, PREFILL_MIRRORED_KEYS, PRIVATE_PROFILE_FIELDS, PageSchema, PageStatus, PageType, type PaginatedResponse, PaginationSchema, PostSchema, PostStatus, type PrefillMirroredKey, type PrivateProfileField, ProficiencyLevel, QuestionSchema, QuestionType, ReadAndAcknowledgeQuestionSchema, RecruiterPageLinkSchema, ReferralSource, ReportReason, ReportSchema, ResourceType, SITEMAP_FORMAT, SYSTEM_DATE_FORMAT, SavedSearchSchema, SavedSearchStatus, SignupContextSource, SkillSchema, SocialAccount, SocialAccountSchema, StudentCompletenessSchema, StudyType, SupportedAnswerChoiceTypes, SupportedApplicationReceivePreferences, SupportedCampaignStatuses, SupportedCompensationTypes, SupportedContactTypes, SupportedEducationLevels, SupportedEmployeeCounts, SupportedEmploymentTypes, SupportedExperienceLevels, SupportedFeedbackStatuses, SupportedFeedbackTypes, SupportedJobAlertFrequencies, SupportedJobCategories, SupportedJobIndustries, SupportedJobSearchUrgencies, SupportedJobStatuses, SupportedJobSubCategories, SupportedNotificationChannels, SupportedPageStatuses, SupportedPageTypes, SupportedPostStatuses, SupportedProficiencyLevels, SupportedQuestionTypes, SupportedReferralSources, SupportedReportReasons, SupportedResourceTypes, SupportedSalaryCurrencies, type SupportedSalaryCurrency, SupportedSavedSearchStatuses, SupportedSignupContextSources, SupportedSocialAccounts, SupportedStudyTypes, SupportedUserProfileVisibilities, SupportedUserRoles, SupportedUserStatuses, SupportedWorkModes, TAXONOMY_LABELS, type TChoiceQuestionSchema, type TDurationSchema, type TInputQuestionSchema, type TJobSchema, type TPostSchema, type TQuestionSchema, type TReadAndAcknowledgeQuestionSchema, type TUserIdAndCreatedAtSchema, TermsAcceptedSchema, UserAdditionalInfoSchema, UserCertificationSchema, UserCompletenessSchema, UserCoordinatorProfileSchema, UserDetailType, UserGeneralDetailSchema, UserIdAndCreatedAtSchema, UserInterestSchema, UserJobPreferencesSchema, UserLanguageSchema, type UserProfileOverview, UserProfileVisibility, UserProjectSchema, UserRecruiterProfileSchema, UserRole, UserSchema, UserSkillSchema, UserStatus, WorkExperienceSchema, WorkMode, categoryOfIndustry, categoryOfSubCategory, dateString, deriveIndustry, getSchemaByQuestion, industriesOfCategory, industryOfSubCategory, subCategoriesOfIndustry };
|
package/dist/index.d.ts
CHANGED
|
@@ -437,6 +437,8 @@ declare const JobSchema: ObjectSchema<{
|
|
|
437
437
|
employmentType: string;
|
|
438
438
|
workMode: string;
|
|
439
439
|
skills: {
|
|
440
|
+
conceptId?: string | null | undefined;
|
|
441
|
+
conceptName?: string | null | undefined;
|
|
440
442
|
name: string;
|
|
441
443
|
logo: {
|
|
442
444
|
dark?: string | null | undefined;
|
|
@@ -1500,6 +1502,24 @@ declare enum UserProfileVisibility {
|
|
|
1500
1502
|
Hidden = "hidden"
|
|
1501
1503
|
}
|
|
1502
1504
|
declare const SupportedUserProfileVisibilities: UserProfileVisibility[];
|
|
1505
|
+
/**
|
|
1506
|
+
* How a user's `signupContext` was obtained. Stored on the document, so these
|
|
1507
|
+
* strings are data: lowercase, and a rename is a migration.
|
|
1508
|
+
*
|
|
1509
|
+
* The distinction is deliberate. Only `CfHeader` is measured; the other two are
|
|
1510
|
+
* inferred, and `Default` is a bare presumption with no signal behind it at all.
|
|
1511
|
+
* Collapsing them would make an assumed country indistinguishable from an
|
|
1512
|
+
* observed one, which is precisely what a compliance record must not do.
|
|
1513
|
+
*/
|
|
1514
|
+
declare enum SignupContextSource {
|
|
1515
|
+
/** Measured from Cloudflare's `cf-ipcountry` header at sign-up. */
|
|
1516
|
+
CfHeader = "cf-header",
|
|
1517
|
+
/** Derived from the user's self-entered profile location (backfill). */
|
|
1518
|
+
Location = "location",
|
|
1519
|
+
/** Assumed; no signal was available. A presumption, not evidence. */
|
|
1520
|
+
Default = "default"
|
|
1521
|
+
}
|
|
1522
|
+
declare const SupportedSignupContextSources: SignupContextSource[];
|
|
1503
1523
|
declare enum UserDetailType {
|
|
1504
1524
|
Overview = "overview",
|
|
1505
1525
|
AdditionalInfo = "additionalInfo",
|
|
@@ -1718,6 +1738,8 @@ declare const FeedbackSchema: yup.ObjectSchema<{
|
|
|
1718
1738
|
|
|
1719
1739
|
declare const SkillSchema: yup.ObjectSchema<{
|
|
1720
1740
|
name: string;
|
|
1741
|
+
conceptId: string | null | undefined;
|
|
1742
|
+
conceptName: string | null | undefined;
|
|
1721
1743
|
logo: {
|
|
1722
1744
|
dark?: string | null | undefined;
|
|
1723
1745
|
light: string;
|
|
@@ -1731,6 +1753,8 @@ declare const SkillSchema: yup.ObjectSchema<{
|
|
|
1731
1753
|
updatedAt: number | undefined;
|
|
1732
1754
|
}, yup.AnyObject, {
|
|
1733
1755
|
name: undefined;
|
|
1756
|
+
conceptId: undefined;
|
|
1757
|
+
conceptName: undefined;
|
|
1734
1758
|
logo: null;
|
|
1735
1759
|
tags: "";
|
|
1736
1760
|
shortId: undefined;
|
|
@@ -1932,9 +1956,11 @@ declare const UserSchema: yup.ObjectSchema<{
|
|
|
1932
1956
|
coordinates: number[];
|
|
1933
1957
|
};
|
|
1934
1958
|
};
|
|
1935
|
-
|
|
1959
|
+
signupContext: {
|
|
1936
1960
|
country: string;
|
|
1937
|
-
|
|
1961
|
+
at: number;
|
|
1962
|
+
locale: string;
|
|
1963
|
+
source: NonNullable<SignupContextSource | undefined>;
|
|
1938
1964
|
} | undefined;
|
|
1939
1965
|
profileVisibility: UserProfileVisibility;
|
|
1940
1966
|
openToWork: boolean;
|
|
@@ -2137,7 +2163,7 @@ declare const UserSchema: yup.ObjectSchema<{
|
|
|
2137
2163
|
coordinates: "";
|
|
2138
2164
|
};
|
|
2139
2165
|
};
|
|
2140
|
-
|
|
2166
|
+
signupContext: undefined;
|
|
2141
2167
|
profileVisibility: UserProfileVisibility.Public;
|
|
2142
2168
|
openToWork: false;
|
|
2143
2169
|
jobSearchUrgency: undefined;
|
|
@@ -2189,9 +2215,11 @@ declare const UserGeneralDetailSchema: yup.ObjectSchema<{
|
|
|
2189
2215
|
coordinates: number[];
|
|
2190
2216
|
};
|
|
2191
2217
|
};
|
|
2192
|
-
|
|
2218
|
+
signupContext: {
|
|
2193
2219
|
country: string;
|
|
2194
|
-
|
|
2220
|
+
at: number;
|
|
2221
|
+
locale: string;
|
|
2222
|
+
source: NonNullable<SignupContextSource | undefined>;
|
|
2195
2223
|
} | undefined;
|
|
2196
2224
|
profileVisibility: UserProfileVisibility;
|
|
2197
2225
|
}, yup.AnyObject, {
|
|
@@ -2221,7 +2249,7 @@ declare const UserGeneralDetailSchema: yup.ObjectSchema<{
|
|
|
2221
2249
|
coordinates: "";
|
|
2222
2250
|
};
|
|
2223
2251
|
};
|
|
2224
|
-
|
|
2252
|
+
signupContext: undefined;
|
|
2225
2253
|
profileVisibility: UserProfileVisibility.Public;
|
|
2226
2254
|
}, "">;
|
|
2227
2255
|
|
|
@@ -2995,4 +3023,4 @@ declare const StudentCompletenessSchema: yup.ObjectSchema<{
|
|
|
2995
3023
|
};
|
|
2996
3024
|
}, "">;
|
|
2997
3025
|
|
|
2998
|
-
export { AnswerChoiceType, ApplicationReceivePreference, CampaignSchema, CampaignStatus, ChoiceQuestionSchema, Common, CompensationType, CompletenessScoreSchema, ContactTypes, CoordinatorPageLinkSchema, DISPLAY_DATE_FORMAT, DISPLAY_DATE_FORMAT_SHORT, DateStringSchema, DbDefaultSchema, DefaultPaginatedResponse, DefaultPaginationOptions, DefaultUserRoles, DurationSchema, EMPTY_STRING, EducationLevel, EducationSchema, EmployeeCount, EmploymentType, ExperienceLevel, FEEDBACK_MESSAGE_MAX, FeedbackSchema, FeedbackStatus, FeedbackType, GeneraDetailFields, GroupManagedBy, GroupMembershipSchema, GroupMembershipStatus, GroupSchema, GroupStatus, GroupTranslationSchema, GroupVisibility, InputQuestionSchema, JOB_TAXONOMY, JobAlertFrequency, JobCategory, JobIndustry, JobSchema, JobSearchUrgency, JobStatus, JobSubCategory, ListFilterSchema, LocationSchema, MIN_SALARY_LOWER_BOUND, MIN_SALARY_UPPER_BOUND, MailingListSchema, NotificationChannel, PREFILL_MIRRORED_KEYS, PRIVATE_PROFILE_FIELDS, PageSchema, PageStatus, PageType, type PaginatedResponse, PaginationSchema, PostSchema, PostStatus, type PrefillMirroredKey, type PrivateProfileField, ProficiencyLevel, QuestionSchema, QuestionType, ReadAndAcknowledgeQuestionSchema, RecruiterPageLinkSchema, ReferralSource, ReportReason, ReportSchema, ResourceType, SITEMAP_FORMAT, SYSTEM_DATE_FORMAT, SavedSearchSchema, SavedSearchStatus, SkillSchema, SocialAccount, SocialAccountSchema, StudentCompletenessSchema, StudyType, SupportedAnswerChoiceTypes, SupportedApplicationReceivePreferences, SupportedCampaignStatuses, SupportedCompensationTypes, SupportedContactTypes, SupportedEducationLevels, SupportedEmployeeCounts, SupportedEmploymentTypes, SupportedExperienceLevels, SupportedFeedbackStatuses, SupportedFeedbackTypes, SupportedJobAlertFrequencies, SupportedJobCategories, SupportedJobIndustries, SupportedJobSearchUrgencies, SupportedJobStatuses, SupportedJobSubCategories, SupportedNotificationChannels, SupportedPageStatuses, SupportedPageTypes, SupportedPostStatuses, SupportedProficiencyLevels, SupportedQuestionTypes, SupportedReferralSources, SupportedReportReasons, SupportedResourceTypes, SupportedSalaryCurrencies, type SupportedSalaryCurrency, SupportedSavedSearchStatuses, SupportedSocialAccounts, SupportedStudyTypes, SupportedUserProfileVisibilities, SupportedUserRoles, SupportedUserStatuses, SupportedWorkModes, TAXONOMY_LABELS, type TChoiceQuestionSchema, type TDurationSchema, type TInputQuestionSchema, type TJobSchema, type TPostSchema, type TQuestionSchema, type TReadAndAcknowledgeQuestionSchema, type TUserIdAndCreatedAtSchema, TermsAcceptedSchema, UserAdditionalInfoSchema, UserCertificationSchema, UserCompletenessSchema, UserCoordinatorProfileSchema, UserDetailType, UserGeneralDetailSchema, UserIdAndCreatedAtSchema, UserInterestSchema, UserJobPreferencesSchema, UserLanguageSchema, type UserProfileOverview, UserProfileVisibility, UserProjectSchema, UserRecruiterProfileSchema, UserRole, UserSchema, UserSkillSchema, UserStatus, WorkExperienceSchema, WorkMode, categoryOfIndustry, categoryOfSubCategory, dateString, deriveIndustry, getSchemaByQuestion, industriesOfCategory, industryOfSubCategory, subCategoriesOfIndustry };
|
|
3026
|
+
export { AnswerChoiceType, ApplicationReceivePreference, CampaignSchema, CampaignStatus, ChoiceQuestionSchema, Common, CompensationType, CompletenessScoreSchema, ContactTypes, CoordinatorPageLinkSchema, DISPLAY_DATE_FORMAT, DISPLAY_DATE_FORMAT_SHORT, DateStringSchema, DbDefaultSchema, DefaultPaginatedResponse, DefaultPaginationOptions, DefaultUserRoles, DurationSchema, EMPTY_STRING, EducationLevel, EducationSchema, EmployeeCount, EmploymentType, ExperienceLevel, FEEDBACK_MESSAGE_MAX, FeedbackSchema, FeedbackStatus, FeedbackType, GeneraDetailFields, GroupManagedBy, GroupMembershipSchema, GroupMembershipStatus, GroupSchema, GroupStatus, GroupTranslationSchema, GroupVisibility, InputQuestionSchema, JOB_TAXONOMY, JobAlertFrequency, JobCategory, JobIndustry, JobSchema, JobSearchUrgency, JobStatus, JobSubCategory, ListFilterSchema, LocationSchema, MIN_SALARY_LOWER_BOUND, MIN_SALARY_UPPER_BOUND, MailingListSchema, NotificationChannel, PREFILL_MIRRORED_KEYS, PRIVATE_PROFILE_FIELDS, PageSchema, PageStatus, PageType, type PaginatedResponse, PaginationSchema, PostSchema, PostStatus, type PrefillMirroredKey, type PrivateProfileField, ProficiencyLevel, QuestionSchema, QuestionType, ReadAndAcknowledgeQuestionSchema, RecruiterPageLinkSchema, ReferralSource, ReportReason, ReportSchema, ResourceType, SITEMAP_FORMAT, SYSTEM_DATE_FORMAT, SavedSearchSchema, SavedSearchStatus, SignupContextSource, SkillSchema, SocialAccount, SocialAccountSchema, StudentCompletenessSchema, StudyType, SupportedAnswerChoiceTypes, SupportedApplicationReceivePreferences, SupportedCampaignStatuses, SupportedCompensationTypes, SupportedContactTypes, SupportedEducationLevels, SupportedEmployeeCounts, SupportedEmploymentTypes, SupportedExperienceLevels, SupportedFeedbackStatuses, SupportedFeedbackTypes, SupportedJobAlertFrequencies, SupportedJobCategories, SupportedJobIndustries, SupportedJobSearchUrgencies, SupportedJobStatuses, SupportedJobSubCategories, SupportedNotificationChannels, SupportedPageStatuses, SupportedPageTypes, SupportedPostStatuses, SupportedProficiencyLevels, SupportedQuestionTypes, SupportedReferralSources, SupportedReportReasons, SupportedResourceTypes, SupportedSalaryCurrencies, type SupportedSalaryCurrency, SupportedSavedSearchStatuses, SupportedSignupContextSources, SupportedSocialAccounts, SupportedStudyTypes, SupportedUserProfileVisibilities, SupportedUserRoles, SupportedUserStatuses, SupportedWorkModes, TAXONOMY_LABELS, type TChoiceQuestionSchema, type TDurationSchema, type TInputQuestionSchema, type TJobSchema, type TPostSchema, type TQuestionSchema, type TReadAndAcknowledgeQuestionSchema, type TUserIdAndCreatedAtSchema, TermsAcceptedSchema, UserAdditionalInfoSchema, UserCertificationSchema, UserCompletenessSchema, UserCoordinatorProfileSchema, UserDetailType, UserGeneralDetailSchema, UserIdAndCreatedAtSchema, UserInterestSchema, UserJobPreferencesSchema, UserLanguageSchema, type UserProfileOverview, UserProfileVisibility, UserProjectSchema, UserRecruiterProfileSchema, UserRole, UserSchema, UserSkillSchema, UserStatus, WorkExperienceSchema, WorkMode, categoryOfIndustry, categoryOfSubCategory, dateString, deriveIndustry, getSchemaByQuestion, industriesOfCategory, industryOfSubCategory, subCategoriesOfIndustry };
|
package/dist/index.js
CHANGED
|
@@ -417,6 +417,26 @@ var PageSchema = object10().shape({
|
|
|
417
417
|
import { array as array6, object as object11, string as string8 } from "yup";
|
|
418
418
|
var SkillSchema = object11({
|
|
419
419
|
name: string8().trim().required().label("Skill Name"),
|
|
420
|
+
/**
|
|
421
|
+
* The taxonomy concept this skill resolved to, assigned by thejob-taxonomy-service
|
|
422
|
+
* via `POST /normalize/batch`. Stable slug, e.g. "power-bi".
|
|
423
|
+
*
|
|
424
|
+
* Normalization is ADDITIVE and never authoritative over `name`: the raw string a
|
|
425
|
+
* job was posted with is always preserved, and these two fields are written
|
|
426
|
+
* alongside it. That is what makes a wrong match, a taxonomy re-run, or a service
|
|
427
|
+
* outage recoverable rather than a data loss.
|
|
428
|
+
*
|
|
429
|
+
* Optional and nullable because they genuinely are absent in two normal cases,
|
|
430
|
+
* neither of which is an error:
|
|
431
|
+
* - the term did not resolve (~22% of mentions are a long tail the taxonomy has
|
|
432
|
+
* no concept for yet), and the raw `name` stands alone;
|
|
433
|
+
* - the document predates normalization and has not been re-processed.
|
|
434
|
+
*
|
|
435
|
+
* Consumers must therefore read `conceptName ?? name`, never `conceptName` alone.
|
|
436
|
+
*/
|
|
437
|
+
conceptId: string8().optional().nullable().label("Concept ID"),
|
|
438
|
+
/** Canonical display name for `conceptId`, e.g. "Power BI". See `conceptId`. */
|
|
439
|
+
conceptName: string8().optional().nullable().label("Concept Name"),
|
|
420
440
|
logo: object11({
|
|
421
441
|
light: string8().required().label("Light Logo"),
|
|
422
442
|
dark: string8().optional().nullable().label("Dark Logo")
|
|
@@ -622,7 +642,15 @@ var JobSchema = object16({
|
|
|
622
642
|
designation: string13().optional().nullable().label("Designation"),
|
|
623
643
|
employmentType: string13().required().label("Employment type"),
|
|
624
644
|
workMode: string13().required().label("Work mode"),
|
|
625
|
-
|
|
645
|
+
/**
|
|
646
|
+
* `conceptId`/`conceptName` carry the taxonomy-service normalization. They are
|
|
647
|
+
* picked here (not just `name`/`logo`) because SkillSchema is `.noUnknown()`:
|
|
648
|
+
* an unpicked field that is actually PRESENT fails validation, so omitting them
|
|
649
|
+
* would reject every normalized job.
|
|
650
|
+
*/
|
|
651
|
+
skills: array8().of(
|
|
652
|
+
SkillSchema.pick(["name", "logo", "conceptId", "conceptName"]).required().label("Skill")
|
|
653
|
+
).nullable().optional().label("Skills"),
|
|
626
654
|
bookmarks: array8().of(UserIdAndCreatedAtSchema).optional().label("Bookmarks"),
|
|
627
655
|
embedding: object16({
|
|
628
656
|
vector: array8(number5().required()).optional().label("Embedding vector"),
|
|
@@ -1935,6 +1963,13 @@ var UserProfileVisibility = /* @__PURE__ */ ((UserProfileVisibility2) => {
|
|
|
1935
1963
|
var SupportedUserProfileVisibilities = Object.values(
|
|
1936
1964
|
UserProfileVisibility
|
|
1937
1965
|
);
|
|
1966
|
+
var SignupContextSource = /* @__PURE__ */ ((SignupContextSource2) => {
|
|
1967
|
+
SignupContextSource2["CfHeader"] = "cf-header";
|
|
1968
|
+
SignupContextSource2["Location"] = "location";
|
|
1969
|
+
SignupContextSource2["Default"] = "default";
|
|
1970
|
+
return SignupContextSource2;
|
|
1971
|
+
})(SignupContextSource || {});
|
|
1972
|
+
var SupportedSignupContextSources = Object.values(SignupContextSource);
|
|
1938
1973
|
var UserDetailType = /* @__PURE__ */ ((UserDetailType2) => {
|
|
1939
1974
|
UserDetailType2["Overview"] = "overview";
|
|
1940
1975
|
UserDetailType2["AdditionalInfo"] = "additionalInfo";
|
|
@@ -2253,7 +2288,7 @@ var FeedbackSchema = object22({
|
|
|
2253
2288
|
}).label("Feedback Schema");
|
|
2254
2289
|
|
|
2255
2290
|
// src/user/user.schema.ts
|
|
2256
|
-
import { array as array14, boolean as boolean15, number as
|
|
2291
|
+
import { array as array14, boolean as boolean15, number as number12, object as object34, string as string32 } from "yup";
|
|
2257
2292
|
|
|
2258
2293
|
// src/user/work-experience.schema.ts
|
|
2259
2294
|
import { boolean as boolean12, object as object23, string as string20 } from "yup";
|
|
@@ -2342,7 +2377,7 @@ var UserAdditionalInfoSchema = object29({
|
|
|
2342
2377
|
}).noUnknown().strict().label("User Additional Info");
|
|
2343
2378
|
|
|
2344
2379
|
// src/user/general-detail.schema.ts
|
|
2345
|
-
import { object as object30, string as string28 } from "yup";
|
|
2380
|
+
import { number as number10, object as object30, string as string28 } from "yup";
|
|
2346
2381
|
var UserGeneralDetailSchema = object30({
|
|
2347
2382
|
id: string28().optional().label("ID"),
|
|
2348
2383
|
name: object30().shape({
|
|
@@ -2358,15 +2393,33 @@ var UserGeneralDetailSchema = object30({
|
|
|
2358
2393
|
mobileVerified: string28().nullable().optional().label("Mobile Verified"),
|
|
2359
2394
|
experienceLevel: string28().oneOf(SupportedExperienceLevels).required().label("Experience level"),
|
|
2360
2395
|
location: LocationSchema.required().label("Location"),
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2396
|
+
/**
|
|
2397
|
+
* Where the account was created, captured once and never rewritten.
|
|
2398
|
+
*
|
|
2399
|
+
* Server-owned: listed in user-service's SERVER_CONTROLLED_FIELDS and absent
|
|
2400
|
+
* from every user-writable schema, so a client cannot set or amend it. That
|
|
2401
|
+
* immutability is the point — this answers "which market/jurisdiction governed
|
|
2402
|
+
* this user at sign-up?", which a field the user can PATCH could never do.
|
|
2403
|
+
*
|
|
2404
|
+
* `source` records HOW the value was obtained, because most of it is inferred
|
|
2405
|
+
* rather than measured, and an audit has to be able to tell those apart:
|
|
2406
|
+
* - `cf-header` measured from Cloudflare's `cf-ipcountry` at sign-up
|
|
2407
|
+
* - `location` derived from the user's self-entered profile location
|
|
2408
|
+
* - `default` assumed (no signal available); presumption, not evidence
|
|
2409
|
+
*/
|
|
2410
|
+
signupContext: object30().shape({
|
|
2411
|
+
// ISO 3166-1 alpha-2, uppercase — the shape `cf-ipcountry` returns, so
|
|
2412
|
+
// measured and inferred values are directly comparable.
|
|
2413
|
+
country: string28().trim().uppercase().length(2).required().label("Signup Country"),
|
|
2414
|
+
locale: string28().trim().required().label("Signup Locale"),
|
|
2415
|
+
source: string28().oneOf(SupportedSignupContextSources).required().label("Signup Context Source"),
|
|
2416
|
+
at: number10().required().label("Signup Context At")
|
|
2417
|
+
}).optional().default(void 0).label("Signup Context"),
|
|
2365
2418
|
profileVisibility: string28().oneOf(SupportedUserProfileVisibilities).default("public" /* Public */).label("Profile Visibility")
|
|
2366
2419
|
}).noUnknown().strict().label("General Detail");
|
|
2367
2420
|
|
|
2368
2421
|
// src/user/user-job-preferences.schema.ts
|
|
2369
|
-
import { array as array11, boolean as boolean14, date as date2, number as
|
|
2422
|
+
import { array as array11, boolean as boolean14, date as date2, number as number11, object as object31, string as string29 } from "yup";
|
|
2370
2423
|
var UserJobPreferencesSchema = object31({
|
|
2371
2424
|
/**
|
|
2372
2425
|
* Whether the job seeker is openly signalling availability. Surfaces the
|
|
@@ -2412,7 +2465,7 @@ var UserJobPreferencesSchema = object31({
|
|
|
2412
2465
|
* Defaults to 0 ("any salary") rather than being optional - every job-seeker
|
|
2413
2466
|
* has a floor, even if it's zero.
|
|
2414
2467
|
*/
|
|
2415
|
-
minSalary:
|
|
2468
|
+
minSalary: number11().integer().min(MIN_SALARY_LOWER_BOUND).max(MIN_SALARY_UPPER_BOUND).default(0).required().label("Minimum Salary"),
|
|
2416
2469
|
minSalaryCurrency: string29().oneOf(SupportedSalaryCurrencies).default("USD").required().label("Minimum Salary Currency"),
|
|
2417
2470
|
/**
|
|
2418
2471
|
* Marketing-attribution capture from the final onboarding step.
|
|
@@ -2439,7 +2492,7 @@ var UserJobPreferencesSchema = object31({
|
|
|
2439
2492
|
id: string29().required().label("ID"),
|
|
2440
2493
|
url: string29().required().label("Resume URL"),
|
|
2441
2494
|
filename: string29().trim().max(255).optional().label("Filename"),
|
|
2442
|
-
sizeBytes:
|
|
2495
|
+
sizeBytes: number11().integer().min(0).optional().label("Size (bytes)"),
|
|
2443
2496
|
mimeType: string29().trim().max(120).optional().label("MIME Type"),
|
|
2444
2497
|
uploadedAt: date2().required().label("Uploaded At"),
|
|
2445
2498
|
isPrimary: boolean14().default(false).label("Is Primary")
|
|
@@ -2569,7 +2622,7 @@ var UserSchema = object34({
|
|
|
2569
2622
|
marketingConsent: object34({
|
|
2570
2623
|
subscribed: boolean15().default(false).label("Subscribed to marketing email"),
|
|
2571
2624
|
/** Epoch ms of the last consent change (opt-in or opt-out). */
|
|
2572
|
-
updatedAt:
|
|
2625
|
+
updatedAt: number12().optional().label("Consent updated at"),
|
|
2573
2626
|
/** Where the consent change originated, e.g. `onboarding`, `unsubscribe`, `settings`. */
|
|
2574
2627
|
source: string32().optional().label("Consent source"),
|
|
2575
2628
|
/**
|
|
@@ -2609,7 +2662,7 @@ var UserSchema = object34({
|
|
|
2609
2662
|
* Only generated for public profiles with sufficient completeness (≥60%).
|
|
2610
2663
|
*/
|
|
2611
2664
|
embedding: object34({
|
|
2612
|
-
vector: array14(
|
|
2665
|
+
vector: array14(number12().required()).optional().label("Embedding vector"),
|
|
2613
2666
|
model: string32().optional().label("Embedding model")
|
|
2614
2667
|
}).nullable().optional().label("Embedding")
|
|
2615
2668
|
}).concat(UserGeneralDetailSchema).concat(UserJobPreferencesSchema).concat(UserRecruiterProfileSchema).concat(UserCoordinatorProfileSchema).noUnknown().strict().label("User Schema");
|
|
@@ -2710,6 +2763,7 @@ export {
|
|
|
2710
2763
|
SYSTEM_DATE_FORMAT,
|
|
2711
2764
|
SavedSearchSchema,
|
|
2712
2765
|
SavedSearchStatus,
|
|
2766
|
+
SignupContextSource,
|
|
2713
2767
|
SkillSchema,
|
|
2714
2768
|
SocialAccount,
|
|
2715
2769
|
SocialAccountSchema,
|
|
@@ -2743,6 +2797,7 @@ export {
|
|
|
2743
2797
|
SupportedResourceTypes,
|
|
2744
2798
|
SupportedSalaryCurrencies,
|
|
2745
2799
|
SupportedSavedSearchStatuses,
|
|
2800
|
+
SupportedSignupContextSources,
|
|
2746
2801
|
SupportedSocialAccounts,
|
|
2747
2802
|
SupportedStudyTypes,
|
|
2748
2803
|
SupportedUserProfileVisibilities,
|
package/package.json
CHANGED
package/src/job/job.schema.ts
CHANGED
|
@@ -69,8 +69,18 @@ export const JobSchema = object({
|
|
|
69
69
|
|
|
70
70
|
workMode: string().required().label("Work mode"),
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* `conceptId`/`conceptName` carry the taxonomy-service normalization. They are
|
|
74
|
+
* picked here (not just `name`/`logo`) because SkillSchema is `.noUnknown()`:
|
|
75
|
+
* an unpicked field that is actually PRESENT fails validation, so omitting them
|
|
76
|
+
* would reject every normalized job.
|
|
77
|
+
*/
|
|
72
78
|
skills: array()
|
|
73
|
-
.of(
|
|
79
|
+
.of(
|
|
80
|
+
SkillSchema.pick(["name", "logo", "conceptId", "conceptName"])
|
|
81
|
+
.required()
|
|
82
|
+
.label("Skill"),
|
|
83
|
+
)
|
|
74
84
|
.nullable()
|
|
75
85
|
.optional()
|
|
76
86
|
.label("Skills"),
|
|
@@ -3,6 +3,29 @@ import { DbDefaultSchema } from "../common/common.schema.js";
|
|
|
3
3
|
|
|
4
4
|
export const SkillSchema = object({
|
|
5
5
|
name: string().trim().required().label("Skill Name"),
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The taxonomy concept this skill resolved to, assigned by thejob-taxonomy-service
|
|
9
|
+
* via `POST /normalize/batch`. Stable slug, e.g. "power-bi".
|
|
10
|
+
*
|
|
11
|
+
* Normalization is ADDITIVE and never authoritative over `name`: the raw string a
|
|
12
|
+
* job was posted with is always preserved, and these two fields are written
|
|
13
|
+
* alongside it. That is what makes a wrong match, a taxonomy re-run, or a service
|
|
14
|
+
* outage recoverable rather than a data loss.
|
|
15
|
+
*
|
|
16
|
+
* Optional and nullable because they genuinely are absent in two normal cases,
|
|
17
|
+
* neither of which is an error:
|
|
18
|
+
* - the term did not resolve (~22% of mentions are a long tail the taxonomy has
|
|
19
|
+
* no concept for yet), and the raw `name` stands alone;
|
|
20
|
+
* - the document predates normalization and has not been re-processed.
|
|
21
|
+
*
|
|
22
|
+
* Consumers must therefore read `conceptName ?? name`, never `conceptName` alone.
|
|
23
|
+
*/
|
|
24
|
+
conceptId: string().optional().nullable().label("Concept ID"),
|
|
25
|
+
|
|
26
|
+
/** Canonical display name for `conceptId`, e.g. "Power BI". See `conceptId`. */
|
|
27
|
+
conceptName: string().optional().nullable().label("Concept Name"),
|
|
28
|
+
|
|
6
29
|
logo: object({
|
|
7
30
|
light: string().required().label("Light Logo"),
|
|
8
31
|
dark: string().optional().nullable().label("Dark Logo"),
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { object, string } from "yup";
|
|
1
|
+
import { number, object, string } from "yup";
|
|
2
2
|
import { SupportedExperienceLevels } from "../common/common.constant.js";
|
|
3
3
|
import { LocationSchema } from "../location/location.schema.js";
|
|
4
4
|
import {
|
|
5
|
+
SupportedSignupContextSources,
|
|
5
6
|
SupportedUserProfileVisibilities,
|
|
6
7
|
UserProfileVisibility,
|
|
7
8
|
} from "./user.constant.js";
|
|
@@ -30,14 +31,35 @@ export const UserGeneralDetailSchema = object({
|
|
|
30
31
|
.required()
|
|
31
32
|
.label("Experience level"),
|
|
32
33
|
location: LocationSchema.required().label("Location"),
|
|
33
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Where the account was created, captured once and never rewritten.
|
|
36
|
+
*
|
|
37
|
+
* Server-owned: listed in user-service's SERVER_CONTROLLED_FIELDS and absent
|
|
38
|
+
* from every user-writable schema, so a client cannot set or amend it. That
|
|
39
|
+
* immutability is the point — this answers "which market/jurisdiction governed
|
|
40
|
+
* this user at sign-up?", which a field the user can PATCH could never do.
|
|
41
|
+
*
|
|
42
|
+
* `source` records HOW the value was obtained, because most of it is inferred
|
|
43
|
+
* rather than measured, and an audit has to be able to tell those apart:
|
|
44
|
+
* - `cf-header` measured from Cloudflare's `cf-ipcountry` at sign-up
|
|
45
|
+
* - `location` derived from the user's self-entered profile location
|
|
46
|
+
* - `default` assumed (no signal available); presumption, not evidence
|
|
47
|
+
*/
|
|
48
|
+
signupContext: object()
|
|
34
49
|
.shape({
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
// ISO 3166-1 alpha-2, uppercase — the shape `cf-ipcountry` returns, so
|
|
51
|
+
// measured and inferred values are directly comparable.
|
|
52
|
+
country: string().trim().uppercase().length(2).required().label("Signup Country"),
|
|
53
|
+
locale: string().trim().required().label("Signup Locale"),
|
|
54
|
+
source: string()
|
|
55
|
+
.oneOf(SupportedSignupContextSources)
|
|
56
|
+
.required()
|
|
57
|
+
.label("Signup Context Source"),
|
|
58
|
+
at: number().required().label("Signup Context At"),
|
|
37
59
|
})
|
|
38
60
|
.optional()
|
|
39
61
|
.default(undefined)
|
|
40
|
-
.label("
|
|
62
|
+
.label("Signup Context"),
|
|
41
63
|
profileVisibility: string()
|
|
42
64
|
.oneOf(SupportedUserProfileVisibilities)
|
|
43
65
|
.default(UserProfileVisibility.Public)
|
|
@@ -34,6 +34,26 @@ export const SupportedUserProfileVisibilities = Object.values(
|
|
|
34
34
|
UserProfileVisibility,
|
|
35
35
|
);
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* How a user's `signupContext` was obtained. Stored on the document, so these
|
|
39
|
+
* strings are data: lowercase, and a rename is a migration.
|
|
40
|
+
*
|
|
41
|
+
* The distinction is deliberate. Only `CfHeader` is measured; the other two are
|
|
42
|
+
* inferred, and `Default` is a bare presumption with no signal behind it at all.
|
|
43
|
+
* Collapsing them would make an assumed country indistinguishable from an
|
|
44
|
+
* observed one, which is precisely what a compliance record must not do.
|
|
45
|
+
*/
|
|
46
|
+
export enum SignupContextSource {
|
|
47
|
+
/** Measured from Cloudflare's `cf-ipcountry` header at sign-up. */
|
|
48
|
+
CfHeader = "cf-header",
|
|
49
|
+
/** Derived from the user's self-entered profile location (backfill). */
|
|
50
|
+
Location = "location",
|
|
51
|
+
/** Assumed; no signal was available. A presumption, not evidence. */
|
|
52
|
+
Default = "default",
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const SupportedSignupContextSources = Object.values(SignupContextSource);
|
|
56
|
+
|
|
37
57
|
export enum UserDetailType {
|
|
38
58
|
Overview = "overview",
|
|
39
59
|
AdditionalInfo = "additionalInfo",
|