@thejob/schema 2.1.1 → 2.1.3
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 +199 -119
- package/dist/index.d.cts +72 -1
- package/dist/index.d.ts +72 -1
- package/dist/index.js +164 -92
- package/package.json +1 -1
- package/src/feedback/feedback.constant.ts +21 -0
- package/src/feedback/feedback.schema.ts +32 -0
- package/src/index.ts +6 -0
- package/src/user/user.constant.ts +23 -0
- package/src/user/user.schema.ts +27 -0
package/dist/index.js
CHANGED
|
@@ -1985,6 +1985,14 @@ var JobAlertFrequency = /* @__PURE__ */ ((JobAlertFrequency2) => {
|
|
|
1985
1985
|
return JobAlertFrequency2;
|
|
1986
1986
|
})(JobAlertFrequency || {});
|
|
1987
1987
|
var SupportedJobAlertFrequencies = Object.values(JobAlertFrequency);
|
|
1988
|
+
var NotificationChannel = /* @__PURE__ */ ((NotificationChannel2) => {
|
|
1989
|
+
NotificationChannel2["GroupActivity"] = "groupActivity";
|
|
1990
|
+
NotificationChannel2["JobActivity"] = "jobActivity";
|
|
1991
|
+
NotificationChannel2["ApplicationUpdates"] = "applicationUpdates";
|
|
1992
|
+
NotificationChannel2["ProductUpdates"] = "productUpdates";
|
|
1993
|
+
return NotificationChannel2;
|
|
1994
|
+
})(NotificationChannel || {});
|
|
1995
|
+
var SupportedNotificationChannels = Object.values(NotificationChannel);
|
|
1988
1996
|
var SupportedSalaryCurrencies = ["USD", "EUR", "GBP", "SEK", "INR"];
|
|
1989
1997
|
var MIN_SALARY_LOWER_BOUND = 0;
|
|
1990
1998
|
var MIN_SALARY_UPPER_BOUND = 1e6;
|
|
@@ -2151,55 +2159,87 @@ var ReportSchema = object20({
|
|
|
2151
2159
|
comment: string17().max(1e3).optional().label("Comment")
|
|
2152
2160
|
}).label("Report Schema");
|
|
2153
2161
|
|
|
2162
|
+
// src/feedback/feedback.schema.ts
|
|
2163
|
+
import { mixed as mixed5, object as object21, string as string18 } from "yup";
|
|
2164
|
+
|
|
2165
|
+
// src/feedback/feedback.constant.ts
|
|
2166
|
+
var FeedbackType = /* @__PURE__ */ ((FeedbackType3) => {
|
|
2167
|
+
FeedbackType3["Bug"] = "bug";
|
|
2168
|
+
FeedbackType3["Feature"] = "feature";
|
|
2169
|
+
FeedbackType3["Suggestion"] = "suggestion";
|
|
2170
|
+
FeedbackType3["Report"] = "report";
|
|
2171
|
+
FeedbackType3["Other"] = "other";
|
|
2172
|
+
return FeedbackType3;
|
|
2173
|
+
})(FeedbackType || {});
|
|
2174
|
+
var SupportedFeedbackTypes = Object.values(FeedbackType);
|
|
2175
|
+
var FeedbackStatus = /* @__PURE__ */ ((FeedbackStatus3) => {
|
|
2176
|
+
FeedbackStatus3["New"] = "new";
|
|
2177
|
+
FeedbackStatus3["InReview"] = "in_review";
|
|
2178
|
+
FeedbackStatus3["Resolved"] = "resolved";
|
|
2179
|
+
FeedbackStatus3["Archived"] = "archived";
|
|
2180
|
+
return FeedbackStatus3;
|
|
2181
|
+
})(FeedbackStatus || {});
|
|
2182
|
+
var SupportedFeedbackStatuses = Object.values(FeedbackStatus);
|
|
2183
|
+
var FEEDBACK_MESSAGE_MAX = 4e3;
|
|
2184
|
+
|
|
2185
|
+
// src/feedback/feedback.schema.ts
|
|
2186
|
+
var FeedbackSchema = object21({
|
|
2187
|
+
type: mixed5().oneOf(SupportedFeedbackTypes).required().label("Type"),
|
|
2188
|
+
message: string18().trim().required("Please enter a message").max(FEEDBACK_MESSAGE_MAX).label("Message"),
|
|
2189
|
+
email: string18().email().optional().label("Email"),
|
|
2190
|
+
pageUrl: string18().optional().label("Page URL"),
|
|
2191
|
+
status: mixed5().oneOf(SupportedFeedbackStatuses).optional().label("Status")
|
|
2192
|
+
}).label("Feedback Schema");
|
|
2193
|
+
|
|
2154
2194
|
// src/user/user.schema.ts
|
|
2155
|
-
import { array as array14, boolean as boolean15, number as number10, object as
|
|
2195
|
+
import { array as array14, boolean as boolean15, number as number10, object as object33, string as string31 } from "yup";
|
|
2156
2196
|
|
|
2157
2197
|
// src/user/work-experience.schema.ts
|
|
2158
|
-
import { boolean as boolean12, object as
|
|
2159
|
-
var WorkExperienceSchema =
|
|
2198
|
+
import { boolean as boolean12, object as object22, string as string19 } from "yup";
|
|
2199
|
+
var WorkExperienceSchema = object22().shape({
|
|
2160
2200
|
company: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Company"),
|
|
2161
|
-
designation:
|
|
2201
|
+
designation: string19().trim().required().label("Designation"),
|
|
2162
2202
|
duration: DurationSchema({
|
|
2163
2203
|
format: "YYYY-MM",
|
|
2164
2204
|
startLabel: "Start Date",
|
|
2165
2205
|
endLabel: "End Date"
|
|
2166
2206
|
}).required().label("Duration"),
|
|
2167
|
-
description:
|
|
2207
|
+
description: string19().trim().max(3e3).optional().label("Description"),
|
|
2168
2208
|
location: LocationSchema.required().label("Location"),
|
|
2169
2209
|
isRemote: boolean12().oneOf([true, false]).default(false).label("Is Remote")
|
|
2170
2210
|
}).noUnknown().strict().label("Work Experience");
|
|
2171
2211
|
|
|
2172
2212
|
// src/user/education.schema.ts
|
|
2173
|
-
import { boolean as boolean13, object as
|
|
2174
|
-
var EducationSchema =
|
|
2213
|
+
import { boolean as boolean13, object as object23, string as string20 } from "yup";
|
|
2214
|
+
var EducationSchema = object23({
|
|
2175
2215
|
institute: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Institute"),
|
|
2176
|
-
course:
|
|
2177
|
-
fieldOfStudy:
|
|
2216
|
+
course: string20().trim().required().label("Course"),
|
|
2217
|
+
fieldOfStudy: string20().trim().required().label("Field of Study"),
|
|
2178
2218
|
duration: DurationSchema({
|
|
2179
2219
|
format: "YYYY-MM",
|
|
2180
2220
|
startLabel: "Start Date",
|
|
2181
2221
|
endLabel: "End Date"
|
|
2182
2222
|
}).required().label("Duration"),
|
|
2183
|
-
description:
|
|
2223
|
+
description: string20().trim().max(3e3).optional().label("Description"),
|
|
2184
2224
|
isDistanceLearning: boolean13().default(false).label("Is Distance Learning"),
|
|
2185
2225
|
location: LocationSchema.required().label("Location"),
|
|
2186
|
-
studyType:
|
|
2226
|
+
studyType: string20().oneOf(SupportedStudyTypes).trim().max(50).required().label("Study Type")
|
|
2187
2227
|
}).noUnknown().strict().label("Education");
|
|
2188
2228
|
|
|
2189
2229
|
// src/user/user-skill.schema.ts
|
|
2190
|
-
import { object as
|
|
2191
|
-
var UserSkillSchema =
|
|
2192
|
-
name:
|
|
2193
|
-
proficiencyLevel:
|
|
2230
|
+
import { object as object24, string as string21 } from "yup";
|
|
2231
|
+
var UserSkillSchema = object24({
|
|
2232
|
+
name: string21().required().label("Name"),
|
|
2233
|
+
proficiencyLevel: string21().oneOf(SupportedProficiencyLevels).required().label("Proficiency Level"),
|
|
2194
2234
|
lastUsed: dateString().format("YYYY-MM").nullable().optional().label("Last Used")
|
|
2195
2235
|
}).noUnknown().strict().label("Skill");
|
|
2196
2236
|
|
|
2197
2237
|
// src/user/project.schema.ts
|
|
2198
|
-
import { object as
|
|
2199
|
-
var UserProjectSchema =
|
|
2200
|
-
name:
|
|
2201
|
-
url:
|
|
2202
|
-
description:
|
|
2238
|
+
import { object as object25, string as string22 } from "yup";
|
|
2239
|
+
var UserProjectSchema = object25({
|
|
2240
|
+
name: string22().trim().max(50).required().label("Name"),
|
|
2241
|
+
url: string22().optional().label("URL"),
|
|
2242
|
+
description: string22().trim().min(100).max(3e3).required().label("Description"),
|
|
2203
2243
|
duration: DurationSchema({
|
|
2204
2244
|
format: "YYYY-MM",
|
|
2205
2245
|
startLabel: "Start Date",
|
|
@@ -2208,65 +2248,65 @@ var UserProjectSchema = object24({
|
|
|
2208
2248
|
}).noUnknown().strict().label("Project");
|
|
2209
2249
|
|
|
2210
2250
|
// src/user/user-certification.schema.ts
|
|
2211
|
-
import { object as
|
|
2212
|
-
var UserCertificationSchema =
|
|
2213
|
-
name:
|
|
2251
|
+
import { object as object26, string as string23 } from "yup";
|
|
2252
|
+
var UserCertificationSchema = object26({
|
|
2253
|
+
name: string23().trim().max(100).required().label("Name"),
|
|
2214
2254
|
// TODO: Add validation for authority
|
|
2215
|
-
authority:
|
|
2216
|
-
licenseNumber:
|
|
2255
|
+
authority: string23().trim().max(100).required().label("Authority"),
|
|
2256
|
+
licenseNumber: string23().trim().max(50).optional().label("License Number"),
|
|
2217
2257
|
duration: DurationSchema({
|
|
2218
2258
|
format: "YYYY-MM",
|
|
2219
2259
|
startLabel: "Start Date",
|
|
2220
2260
|
endLabel: "End Date"
|
|
2221
2261
|
}).optional().nullable().label("Duration"),
|
|
2222
|
-
url:
|
|
2262
|
+
url: string23().optional().label("URL")
|
|
2223
2263
|
}).noUnknown().strict().label("Certification");
|
|
2224
2264
|
|
|
2225
2265
|
// src/user/user-interest.schema.ts
|
|
2226
|
-
import { string as
|
|
2227
|
-
var UserInterestSchema =
|
|
2266
|
+
import { string as string24 } from "yup";
|
|
2267
|
+
var UserInterestSchema = string24().trim().required().label("Interest");
|
|
2228
2268
|
|
|
2229
2269
|
// src/user/user-language.schema.ts
|
|
2230
|
-
import { object as
|
|
2231
|
-
var UserLanguageSchema =
|
|
2232
|
-
name:
|
|
2233
|
-
proficiencyLevel:
|
|
2270
|
+
import { object as object27, string as string25 } from "yup";
|
|
2271
|
+
var UserLanguageSchema = object27({
|
|
2272
|
+
name: string25().trim().required().label("Name"),
|
|
2273
|
+
proficiencyLevel: string25().oneOf(SupportedProficiencyLevels).trim().max(50).required().label("Proficiency Level")
|
|
2234
2274
|
}).noUnknown().strict().label("Language");
|
|
2235
2275
|
|
|
2236
2276
|
// src/user/user-additional-info.schema.ts
|
|
2237
|
-
import { object as
|
|
2238
|
-
var UserAdditionalInfoSchema =
|
|
2239
|
-
title:
|
|
2240
|
-
description:
|
|
2277
|
+
import { object as object28, string as string26 } from "yup";
|
|
2278
|
+
var UserAdditionalInfoSchema = object28({
|
|
2279
|
+
title: string26().trim().max(60).required().label("Title"),
|
|
2280
|
+
description: string26().trim().max(1e3).required().label("Description")
|
|
2241
2281
|
}).noUnknown().strict().label("User Additional Info");
|
|
2242
2282
|
|
|
2243
2283
|
// src/user/general-detail.schema.ts
|
|
2244
|
-
import { object as
|
|
2245
|
-
var UserGeneralDetailSchema =
|
|
2246
|
-
id:
|
|
2247
|
-
name:
|
|
2248
|
-
first:
|
|
2249
|
-
last:
|
|
2284
|
+
import { object as object29, string as string27 } from "yup";
|
|
2285
|
+
var UserGeneralDetailSchema = object29({
|
|
2286
|
+
id: string27().optional().label("ID"),
|
|
2287
|
+
name: object29().shape({
|
|
2288
|
+
first: string27().trim().max(50).required().label("First Name"),
|
|
2289
|
+
last: string27().trim().max(50).optional().label("Last Name")
|
|
2250
2290
|
}).required().label("Name"),
|
|
2251
|
-
headline:
|
|
2252
|
-
image:
|
|
2253
|
-
aboutMe:
|
|
2254
|
-
email:
|
|
2255
|
-
mobile:
|
|
2256
|
-
emailVerified:
|
|
2257
|
-
mobileVerified:
|
|
2258
|
-
experienceLevel:
|
|
2291
|
+
headline: string27().trim().max(200).optional().label("Headline"),
|
|
2292
|
+
image: string27().optional().label("Image"),
|
|
2293
|
+
aboutMe: string27().trim().max(3e3).optional().label("About Me"),
|
|
2294
|
+
email: string27().required().label("Email"),
|
|
2295
|
+
mobile: string27().nullable().optional().label("Mobile"),
|
|
2296
|
+
emailVerified: string27().nullable().optional().label("Email Verified"),
|
|
2297
|
+
mobileVerified: string27().nullable().optional().label("Mobile Verified"),
|
|
2298
|
+
experienceLevel: string27().oneOf(SupportedExperienceLevels).required().label("Experience level"),
|
|
2259
2299
|
location: LocationSchema.required().label("Location"),
|
|
2260
|
-
region:
|
|
2261
|
-
country:
|
|
2262
|
-
lang:
|
|
2300
|
+
region: object29().shape({
|
|
2301
|
+
country: string27().trim().required().label("Region Country"),
|
|
2302
|
+
lang: string27().trim().required().label("Region Language")
|
|
2263
2303
|
}).optional().default(void 0).label("Region"),
|
|
2264
|
-
profileVisibility:
|
|
2304
|
+
profileVisibility: string27().oneOf(SupportedUserProfileVisibilities).default("public" /* Public */).label("Profile Visibility")
|
|
2265
2305
|
}).noUnknown().strict().label("General Detail");
|
|
2266
2306
|
|
|
2267
2307
|
// src/user/user-job-preferences.schema.ts
|
|
2268
|
-
import { array as array11, boolean as boolean14, date as date2, number as number9, object as
|
|
2269
|
-
var UserJobPreferencesSchema =
|
|
2308
|
+
import { array as array11, boolean as boolean14, date as date2, number as number9, object as object30, string as string28 } from "yup";
|
|
2309
|
+
var UserJobPreferencesSchema = object30({
|
|
2270
2310
|
/**
|
|
2271
2311
|
* Whether the job seeker is openly signalling availability. Surfaces the
|
|
2272
2312
|
* "Open to work" badge on the public profile and boosts visibility in
|
|
@@ -2278,7 +2318,7 @@ var UserJobPreferencesSchema = object29({
|
|
|
2278
2318
|
* and can downgrade the visibility of expired or low-relevance postings for
|
|
2279
2319
|
* users in `passively_browsing` mode.
|
|
2280
2320
|
*/
|
|
2281
|
-
jobSearchUrgency:
|
|
2321
|
+
jobSearchUrgency: string28().oneOf(SupportedJobSearchUrgencies).required().label("Job Search Urgency"),
|
|
2282
2322
|
/**
|
|
2283
2323
|
* Locations the user wants to work in. Stores the full `LocationSchema`
|
|
2284
2324
|
* shape (country, city, state, geo, etc.) as returned by the locations
|
|
@@ -2295,7 +2335,7 @@ var UserJobPreferencesSchema = object29({
|
|
|
2295
2335
|
* Both reuse the same `ExperienceLevel` taxonomy from `common.constant.ts`
|
|
2296
2336
|
* so search/match logic doesn't have to translate between two vocabularies.
|
|
2297
2337
|
*/
|
|
2298
|
-
targetExperienceLevels: array11().of(
|
|
2338
|
+
targetExperienceLevels: array11().of(string28().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"),
|
|
2299
2339
|
/**
|
|
2300
2340
|
* Specializations the user is searching for - free-form titles.
|
|
2301
2341
|
*
|
|
@@ -2304,7 +2344,7 @@ var UserJobPreferencesSchema = object29({
|
|
|
2304
2344
|
* surfaces a curated "Popular Roles" picker for discovery, but the user
|
|
2305
2345
|
* can ultimately add any title; matching/normalization happens downstream.
|
|
2306
2346
|
*/
|
|
2307
|
-
jobRoles: array11().of(
|
|
2347
|
+
jobRoles: array11().of(string28().trim().max(120).required()).min(1, "Pick at least one role.").required().label("Job Roles"),
|
|
2308
2348
|
/**
|
|
2309
2349
|
* Minimum acceptable annual base salary, in `minSalaryCurrency`.
|
|
2310
2350
|
* Stored as an integer in the currency's major unit (e.g. dollars, not cents).
|
|
@@ -2312,11 +2352,11 @@ var UserJobPreferencesSchema = object29({
|
|
|
2312
2352
|
* has a floor, even if it's zero.
|
|
2313
2353
|
*/
|
|
2314
2354
|
minSalary: number9().integer().min(MIN_SALARY_LOWER_BOUND).max(MIN_SALARY_UPPER_BOUND).default(0).required().label("Minimum Salary"),
|
|
2315
|
-
minSalaryCurrency:
|
|
2355
|
+
minSalaryCurrency: string28().oneOf(SupportedSalaryCurrencies).default("USD").required().label("Minimum Salary Currency"),
|
|
2316
2356
|
/**
|
|
2317
2357
|
* Marketing-attribution capture from the final onboarding step.
|
|
2318
2358
|
*/
|
|
2319
|
-
referralSource:
|
|
2359
|
+
referralSource: string28().oneOf(SupportedReferralSources).required().label("Referral Source"),
|
|
2320
2360
|
/**
|
|
2321
2361
|
* Resumes the user has uploaded. Embedded directly on the user document
|
|
2322
2362
|
* (capped at 5) - they're cheap to denormalize, every wizard step that
|
|
@@ -2331,15 +2371,15 @@ var UserJobPreferencesSchema = object29({
|
|
|
2331
2371
|
* live in their own collection.
|
|
2332
2372
|
*/
|
|
2333
2373
|
resumes: array11().of(
|
|
2334
|
-
|
|
2374
|
+
object30({
|
|
2335
2375
|
// Stable id assigned on upload - used to address a specific entry
|
|
2336
2376
|
// for delete / set-primary operations without exposing the GCS URL
|
|
2337
2377
|
// as a route key.
|
|
2338
|
-
id:
|
|
2339
|
-
url:
|
|
2340
|
-
filename:
|
|
2378
|
+
id: string28().required().label("ID"),
|
|
2379
|
+
url: string28().required().label("Resume URL"),
|
|
2380
|
+
filename: string28().trim().max(255).optional().label("Filename"),
|
|
2341
2381
|
sizeBytes: number9().integer().min(0).optional().label("Size (bytes)"),
|
|
2342
|
-
mimeType:
|
|
2382
|
+
mimeType: string28().trim().max(120).optional().label("MIME Type"),
|
|
2343
2383
|
uploadedAt: date2().required().label("Uploaded At"),
|
|
2344
2384
|
isPrimary: boolean14().default(false).label("Is Primary")
|
|
2345
2385
|
}).noUnknown().strict().label("Resume")
|
|
@@ -2368,12 +2408,12 @@ var UserJobPreferencesSchema = object29({
|
|
|
2368
2408
|
* APIs. If `user.<field>` is empty there, the correct behavior is empty,
|
|
2369
2409
|
* because the user has not confirmed it yet.
|
|
2370
2410
|
*/
|
|
2371
|
-
pendingPrefill:
|
|
2372
|
-
headline:
|
|
2373
|
-
aboutMe:
|
|
2374
|
-
mobile:
|
|
2411
|
+
pendingPrefill: object30({
|
|
2412
|
+
headline: string28().trim().optional().label("Pending Headline"),
|
|
2413
|
+
aboutMe: string28().trim().optional().label("Pending About Me"),
|
|
2414
|
+
mobile: string28().trim().optional().label("Pending Mobile"),
|
|
2375
2415
|
location: LocationSchema.optional().default(void 0).label("Pending Location"),
|
|
2376
|
-
experienceLevel:
|
|
2416
|
+
experienceLevel: string28().oneOf(SupportedExperienceLevels).optional().label("Pending Experience Level"),
|
|
2377
2417
|
socialAccounts: array11().of(SocialAccountSchema.required()).optional().label("Pending Social Accounts"),
|
|
2378
2418
|
workExperiences: array11().of(WorkExperienceSchema.required()).optional().label("Pending Work Experiences"),
|
|
2379
2419
|
educations: array11().of(EducationSchema.required()).optional().label("Pending Educations")
|
|
@@ -2381,35 +2421,35 @@ var UserJobPreferencesSchema = object29({
|
|
|
2381
2421
|
}).noUnknown().strict().label("User Job Preferences Schema");
|
|
2382
2422
|
|
|
2383
2423
|
// src/user/user-recruiter-profile.schema.ts
|
|
2384
|
-
import { array as array12, object as
|
|
2385
|
-
var RecruiterPageLinkSchema =
|
|
2424
|
+
import { array as array12, object as object31, string as string29 } from "yup";
|
|
2425
|
+
var RecruiterPageLinkSchema = object31({
|
|
2386
2426
|
page: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Company Page"),
|
|
2387
|
-
jobTitle:
|
|
2427
|
+
jobTitle: string29().trim().max(100).optional().label("Job Title")
|
|
2388
2428
|
}).noUnknown().strict().label("Recruiter Page Link");
|
|
2389
|
-
var UserRecruiterProfileSchema =
|
|
2390
|
-
recruiterProfile:
|
|
2429
|
+
var UserRecruiterProfileSchema = object31({
|
|
2430
|
+
recruiterProfile: object31({
|
|
2391
2431
|
hiringFor: array12().of(RecruiterPageLinkSchema).default([]).label("Hiring For")
|
|
2392
2432
|
}).optional().default(void 0).label("Recruiter Profile")
|
|
2393
2433
|
}).noUnknown().strict().label("User Recruiter Profile");
|
|
2394
2434
|
|
|
2395
2435
|
// src/user/user-coordinator-profile.schema.ts
|
|
2396
|
-
import { array as array13, object as
|
|
2397
|
-
var CoordinatorPageLinkSchema =
|
|
2436
|
+
import { array as array13, object as object32, string as string30 } from "yup";
|
|
2437
|
+
var CoordinatorPageLinkSchema = object32({
|
|
2398
2438
|
page: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Institute Page"),
|
|
2399
|
-
jobTitle:
|
|
2439
|
+
jobTitle: string30().trim().max(100).optional().label("Job Title")
|
|
2400
2440
|
}).noUnknown().strict().label("Coordinator Page Link");
|
|
2401
|
-
var UserCoordinatorProfileSchema =
|
|
2402
|
-
coordinatorProfile:
|
|
2441
|
+
var UserCoordinatorProfileSchema = object32({
|
|
2442
|
+
coordinatorProfile: object32({
|
|
2403
2443
|
coordinatesAt: array13().of(CoordinatorPageLinkSchema).default([]).label("Coordinates At")
|
|
2404
2444
|
}).optional().default(void 0).label("Coordinator Profile")
|
|
2405
2445
|
}).noUnknown().strict().label("User Coordinator Profile");
|
|
2406
2446
|
|
|
2407
2447
|
// src/user/user.schema.ts
|
|
2408
|
-
var UserSchema =
|
|
2448
|
+
var UserSchema = object33({
|
|
2409
2449
|
/**
|
|
2410
2450
|
* Related auth account id from auth Server (e.g. Better auth https://auth.thejob.dev)
|
|
2411
2451
|
*/
|
|
2412
|
-
authAccountId:
|
|
2452
|
+
authAccountId: string31().required().label("Auth Account ID"),
|
|
2413
2453
|
/**
|
|
2414
2454
|
* Social media information about the user (e.g. LinkedIn, GitHub, etc.)
|
|
2415
2455
|
*/
|
|
@@ -2449,11 +2489,11 @@ var UserSchema = object32({
|
|
|
2449
2489
|
/**
|
|
2450
2490
|
* Status of the user account
|
|
2451
2491
|
*/
|
|
2452
|
-
status:
|
|
2492
|
+
status: string31().oneOf(SupportedUserStatuses).required().label("Status"),
|
|
2453
2493
|
/**
|
|
2454
2494
|
* Roles assigned to the user
|
|
2455
2495
|
*/
|
|
2456
|
-
roles: array14().of(
|
|
2496
|
+
roles: array14().of(string31().oneOf(SupportedUserRoles)).required().default(DefaultUserRoles).label("Roles"),
|
|
2457
2497
|
/**
|
|
2458
2498
|
* Explicit consent to receive marketing email (newsletters, job alerts by
|
|
2459
2499
|
* broadcast, product updates). Marketing sends are HARD-GATED on
|
|
@@ -2465,33 +2505,57 @@ var UserSchema = object32({
|
|
|
2465
2505
|
* unsubscribe sets `subscribed=false` so consent and the email-service
|
|
2466
2506
|
* suppression list stay consistent. Defaults to NOT subscribed (opt-in).
|
|
2467
2507
|
*/
|
|
2468
|
-
marketingConsent:
|
|
2508
|
+
marketingConsent: object33({
|
|
2469
2509
|
subscribed: boolean15().default(false).label("Subscribed to marketing email"),
|
|
2470
2510
|
/** Epoch ms of the last consent change (opt-in or opt-out). */
|
|
2471
2511
|
updatedAt: number10().optional().label("Consent updated at"),
|
|
2472
2512
|
/** Where the consent change originated, e.g. `onboarding`, `unsubscribe`, `settings`. */
|
|
2473
|
-
source:
|
|
2513
|
+
source: string31().optional().label("Consent source"),
|
|
2474
2514
|
/**
|
|
2475
2515
|
* How often the user receives job-alert emails. `off` disables job alerts
|
|
2476
2516
|
* only; other marketing stays governed by `subscribed`. Unset means the
|
|
2477
2517
|
* user has not chosen a cadence yet.
|
|
2478
2518
|
*/
|
|
2479
|
-
jobAlertFrequency:
|
|
2519
|
+
jobAlertFrequency: string31().oneOf(SupportedJobAlertFrequencies).optional().label("Job alert frequency")
|
|
2480
2520
|
}).default({ subscribed: false }).label("Marketing Consent"),
|
|
2521
|
+
/**
|
|
2522
|
+
* Per-channel toggles for NOTIFICATION email — mail about activity on the
|
|
2523
|
+
* user's own account (a group invite, a job of theirs expiring).
|
|
2524
|
+
*
|
|
2525
|
+
* Deliberately separate from `marketingConsent`, which they must NOT be
|
|
2526
|
+
* collapsed into: the two have different legal footing, which the
|
|
2527
|
+
* email-service footer rules already encode. Notifications are opt-OUT
|
|
2528
|
+
* (default `true`) because a user who declined marketing still needs to hear
|
|
2529
|
+
* that their group invite arrived; marketing stays opt-IN. `productUpdates`
|
|
2530
|
+
* is the exception — it is promotional in character, so it defaults off.
|
|
2531
|
+
*
|
|
2532
|
+
* Transactional mail (magic-link, verification) ignores this object entirely.
|
|
2533
|
+
*/
|
|
2534
|
+
notificationPrefs: object33({
|
|
2535
|
+
groupActivity: boolean15().default(true).label("Group activity email"),
|
|
2536
|
+
jobActivity: boolean15().default(true).label("Job activity email"),
|
|
2537
|
+
applicationUpdates: boolean15().default(true).label("Application update email"),
|
|
2538
|
+
productUpdates: boolean15().default(false).label("Product update email")
|
|
2539
|
+
}).default({
|
|
2540
|
+
groupActivity: true,
|
|
2541
|
+
jobActivity: true,
|
|
2542
|
+
applicationUpdates: true,
|
|
2543
|
+
productUpdates: false
|
|
2544
|
+
}).label("Notification Preferences"),
|
|
2481
2545
|
/**
|
|
2482
2546
|
* Vector embedding of the user's profile for semantic/hybrid search.
|
|
2483
2547
|
* Generated from a structured summary of skills, experience, education, etc.
|
|
2484
2548
|
* Only generated for public profiles with sufficient completeness (≥60%).
|
|
2485
2549
|
*/
|
|
2486
|
-
embedding:
|
|
2550
|
+
embedding: object33({
|
|
2487
2551
|
vector: array14(number10().required()).optional().label("Embedding vector"),
|
|
2488
|
-
model:
|
|
2552
|
+
model: string31().optional().label("Embedding model")
|
|
2489
2553
|
}).nullable().optional().label("Embedding")
|
|
2490
2554
|
}).concat(UserGeneralDetailSchema).concat(UserJobPreferencesSchema).concat(UserRecruiterProfileSchema).concat(UserCoordinatorProfileSchema).noUnknown().strict().label("User Schema");
|
|
2491
2555
|
|
|
2492
2556
|
// src/user/user-completeness.schema.ts
|
|
2493
|
-
import { object as
|
|
2494
|
-
var UserCompletenessSchema =
|
|
2557
|
+
import { object as object34 } from "yup";
|
|
2558
|
+
var UserCompletenessSchema = object34({
|
|
2495
2559
|
additionalInfo: CompletenessScoreSchema(0).label("Additional Info"),
|
|
2496
2560
|
// Optional
|
|
2497
2561
|
certifications: CompletenessScoreSchema(0).label("Certifications"),
|
|
@@ -2537,6 +2601,10 @@ export {
|
|
|
2537
2601
|
EmployeeCount,
|
|
2538
2602
|
EmploymentType,
|
|
2539
2603
|
ExperienceLevel,
|
|
2604
|
+
FEEDBACK_MESSAGE_MAX,
|
|
2605
|
+
FeedbackSchema,
|
|
2606
|
+
FeedbackStatus,
|
|
2607
|
+
FeedbackType,
|
|
2540
2608
|
GeneraDetailFields,
|
|
2541
2609
|
GroupManagedBy,
|
|
2542
2610
|
GroupMembershipSchema,
|
|
@@ -2559,6 +2627,7 @@ export {
|
|
|
2559
2627
|
MIN_SALARY_LOWER_BOUND,
|
|
2560
2628
|
MIN_SALARY_UPPER_BOUND,
|
|
2561
2629
|
MailingListSchema,
|
|
2630
|
+
NotificationChannel,
|
|
2562
2631
|
PREFILL_MIRRORED_KEYS,
|
|
2563
2632
|
PRIVATE_PROFILE_FIELDS,
|
|
2564
2633
|
PageSchema,
|
|
@@ -2592,12 +2661,15 @@ export {
|
|
|
2592
2661
|
SupportedEmployeeCounts,
|
|
2593
2662
|
SupportedEmploymentTypes,
|
|
2594
2663
|
SupportedExperienceLevels,
|
|
2664
|
+
SupportedFeedbackStatuses,
|
|
2665
|
+
SupportedFeedbackTypes,
|
|
2595
2666
|
SupportedJobAlertFrequencies,
|
|
2596
2667
|
SupportedJobCategories,
|
|
2597
2668
|
SupportedJobIndustries,
|
|
2598
2669
|
SupportedJobSearchUrgencies,
|
|
2599
2670
|
SupportedJobStatuses,
|
|
2600
2671
|
SupportedJobSubCategories,
|
|
2672
|
+
SupportedNotificationChannels,
|
|
2601
2673
|
SupportedPageStatuses,
|
|
2602
2674
|
SupportedPageTypes,
|
|
2603
2675
|
SupportedPostStatuses,
|
package/package.json
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export enum FeedbackType {
|
|
2
|
+
Bug = "bug",
|
|
3
|
+
Feature = "feature",
|
|
4
|
+
Suggestion = "suggestion",
|
|
5
|
+
Report = "report",
|
|
6
|
+
Other = "other",
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const SupportedFeedbackTypes = Object.values(FeedbackType);
|
|
10
|
+
|
|
11
|
+
export enum FeedbackStatus {
|
|
12
|
+
New = "new",
|
|
13
|
+
InReview = "in_review",
|
|
14
|
+
Resolved = "resolved",
|
|
15
|
+
Archived = "archived",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const SupportedFeedbackStatuses = Object.values(FeedbackStatus);
|
|
19
|
+
|
|
20
|
+
/** Max length of a feedback message body (enforced client + server). */
|
|
21
|
+
export const FEEDBACK_MESSAGE_MAX = 4000;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { mixed, object, string } from "yup";
|
|
2
|
+
import {
|
|
3
|
+
FEEDBACK_MESSAGE_MAX,
|
|
4
|
+
FeedbackStatus,
|
|
5
|
+
FeedbackType,
|
|
6
|
+
SupportedFeedbackStatuses,
|
|
7
|
+
SupportedFeedbackTypes,
|
|
8
|
+
} from "./feedback.constant.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A user-submitted feedback / report record (bug, feature request, suggestion,
|
|
12
|
+
* abuse report, …) captured by the app-wide feedback widget and stored by
|
|
13
|
+
* common-service. `email` is an optional reply-to for signed-out submitters;
|
|
14
|
+
* the rest of the identity/diagnostic fields are attached server-side.
|
|
15
|
+
*/
|
|
16
|
+
export const FeedbackSchema = object({
|
|
17
|
+
type: mixed<FeedbackType>()
|
|
18
|
+
.oneOf(SupportedFeedbackTypes)
|
|
19
|
+
.required()
|
|
20
|
+
.label("Type"),
|
|
21
|
+
message: string()
|
|
22
|
+
.trim()
|
|
23
|
+
.required("Please enter a message")
|
|
24
|
+
.max(FEEDBACK_MESSAGE_MAX)
|
|
25
|
+
.label("Message"),
|
|
26
|
+
email: string().email().optional().label("Email"),
|
|
27
|
+
pageUrl: string().optional().label("Page URL"),
|
|
28
|
+
status: mixed<FeedbackStatus>()
|
|
29
|
+
.oneOf(SupportedFeedbackStatuses)
|
|
30
|
+
.optional()
|
|
31
|
+
.label("Status"),
|
|
32
|
+
}).label("Feedback Schema");
|
package/src/index.ts
CHANGED
|
@@ -68,6 +68,12 @@ export * from "./pagination/pagination.constant.js";
|
|
|
68
68
|
export * from "./report/report.schema.js";
|
|
69
69
|
export * from "./report/report.constant.js";
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Feedback schemas and constants (feedback widget + reports inbox).
|
|
73
|
+
*/
|
|
74
|
+
export * from "./feedback/feedback.schema.js";
|
|
75
|
+
export * from "./feedback/feedback.constant.js";
|
|
76
|
+
|
|
71
77
|
/**
|
|
72
78
|
* Skill schemas and constants.
|
|
73
79
|
*/
|
|
@@ -97,6 +97,29 @@ export enum JobAlertFrequency {
|
|
|
97
97
|
|
|
98
98
|
export const SupportedJobAlertFrequencies = Object.values(JobAlertFrequency);
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Channels of NOTIFICATION email — mail about activity on the user's own
|
|
102
|
+
* account (a group invite, a job of theirs expiring). Distinct from marketing:
|
|
103
|
+
* notifications are opt-OUT (default on, see `notificationPrefs`), marketing is
|
|
104
|
+
* opt-IN (`marketingConsent.subscribed`). Transactional mail (magic-link,
|
|
105
|
+
* verification) is governed by neither and always sends.
|
|
106
|
+
*
|
|
107
|
+
* Deliberately coarse: four channels a user will actually reason about, rather
|
|
108
|
+
* than one toggle per email template.
|
|
109
|
+
*/
|
|
110
|
+
export enum NotificationChannel {
|
|
111
|
+
/** Group invites, join requests, membership approved/rejected. */
|
|
112
|
+
GroupActivity = "groupActivity",
|
|
113
|
+
/** Activity on jobs the user posted, e.g. a listing expiring. */
|
|
114
|
+
JobActivity = "jobActivity",
|
|
115
|
+
/** Status changes on jobs the user applied to. */
|
|
116
|
+
ApplicationUpdates = "applicationUpdates",
|
|
117
|
+
/** Product announcements and feature news. */
|
|
118
|
+
ProductUpdates = "productUpdates",
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const SupportedNotificationChannels = Object.values(NotificationChannel);
|
|
122
|
+
|
|
100
123
|
/** ISO-4217 currency codes accepted for the salary preference (extend as needed). */
|
|
101
124
|
export const SupportedSalaryCurrencies = ["USD", "EUR", "GBP", "SEK", "INR"] as const;
|
|
102
125
|
export type SupportedSalaryCurrency = (typeof SupportedSalaryCurrencies)[number];
|
package/src/user/user.schema.ts
CHANGED
|
@@ -141,6 +141,33 @@ export const UserSchema = object({
|
|
|
141
141
|
.default({ subscribed: false })
|
|
142
142
|
.label("Marketing Consent"),
|
|
143
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Per-channel toggles for NOTIFICATION email — mail about activity on the
|
|
146
|
+
* user's own account (a group invite, a job of theirs expiring).
|
|
147
|
+
*
|
|
148
|
+
* Deliberately separate from `marketingConsent`, which they must NOT be
|
|
149
|
+
* collapsed into: the two have different legal footing, which the
|
|
150
|
+
* email-service footer rules already encode. Notifications are opt-OUT
|
|
151
|
+
* (default `true`) because a user who declined marketing still needs to hear
|
|
152
|
+
* that their group invite arrived; marketing stays opt-IN. `productUpdates`
|
|
153
|
+
* is the exception — it is promotional in character, so it defaults off.
|
|
154
|
+
*
|
|
155
|
+
* Transactional mail (magic-link, verification) ignores this object entirely.
|
|
156
|
+
*/
|
|
157
|
+
notificationPrefs: object({
|
|
158
|
+
groupActivity: boolean().default(true).label("Group activity email"),
|
|
159
|
+
jobActivity: boolean().default(true).label("Job activity email"),
|
|
160
|
+
applicationUpdates: boolean().default(true).label("Application update email"),
|
|
161
|
+
productUpdates: boolean().default(false).label("Product update email"),
|
|
162
|
+
})
|
|
163
|
+
.default({
|
|
164
|
+
groupActivity: true,
|
|
165
|
+
jobActivity: true,
|
|
166
|
+
applicationUpdates: true,
|
|
167
|
+
productUpdates: false,
|
|
168
|
+
})
|
|
169
|
+
.label("Notification Preferences"),
|
|
170
|
+
|
|
144
171
|
/**
|
|
145
172
|
* Vector embedding of the user's profile for semantic/hybrid search.
|
|
146
173
|
* Generated from a structured summary of skills, experience, education, etc.
|