@thejob/schema 2.1.3 → 2.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1935,6 +1935,13 @@ var UserProfileVisibility = /* @__PURE__ */ ((UserProfileVisibility2) => {
1935
1935
  var SupportedUserProfileVisibilities = Object.values(
1936
1936
  UserProfileVisibility
1937
1937
  );
1938
+ var SignupContextSource = /* @__PURE__ */ ((SignupContextSource2) => {
1939
+ SignupContextSource2["CfHeader"] = "cf-header";
1940
+ SignupContextSource2["Location"] = "location";
1941
+ SignupContextSource2["Default"] = "default";
1942
+ return SignupContextSource2;
1943
+ })(SignupContextSource || {});
1944
+ var SupportedSignupContextSources = Object.values(SignupContextSource);
1938
1945
  var UserDetailType = /* @__PURE__ */ ((UserDetailType2) => {
1939
1946
  UserDetailType2["Overview"] = "overview";
1940
1947
  UserDetailType2["AdditionalInfo"] = "additionalInfo";
@@ -2127,8 +2134,69 @@ var DefaultPaginationOptions = {
2127
2134
  limit: 10
2128
2135
  };
2129
2136
 
2137
+ // src/saved-search/saved-search.schema.ts
2138
+ import { number as number9, object as object20, string as string17 } from "yup";
2139
+
2140
+ // src/saved-search/saved-search.constant.ts
2141
+ var SavedSearchStatus = /* @__PURE__ */ ((SavedSearchStatus2) => {
2142
+ SavedSearchStatus2["Active"] = "active";
2143
+ SavedSearchStatus2["Paused"] = "paused";
2144
+ return SavedSearchStatus2;
2145
+ })(SavedSearchStatus || {});
2146
+ var SupportedSavedSearchStatuses = Object.values(SavedSearchStatus);
2147
+
2148
+ // src/saved-search/saved-search.schema.ts
2149
+ var SavedSearchSchema = object20({
2150
+ /** Public identifier. Server-assigned. */
2151
+ shortId: string17().trim().label("Short Id"),
2152
+ /**
2153
+ * Owner's Better Auth account id. Server-assigned from the verified token,
2154
+ * never accepted from a request body.
2155
+ */
2156
+ ownerUserId: string17().trim().label("Owner"),
2157
+ /** What the user calls this search, e.g. "Remote React roles". */
2158
+ label: string17().trim().max(120).required().label("Label"),
2159
+ /**
2160
+ * The free-text query. Optional because a search can be filters-only ("all
2161
+ * remote jobs in Sweden"), which is a legitimate alert to want.
2162
+ */
2163
+ search: string17().trim().max(500).optional().label("Search Text"),
2164
+ /**
2165
+ * Field filters, in the same `{ field: values }` shape the search endpoint
2166
+ * parses from `filter[field]=a,b`. Held as a free-form object rather than a
2167
+ * closed schema because the job facets it addresses live in jobs-service and
2168
+ * change independently; validating them here would put this package in the
2169
+ * business of tracking that vocabulary.
2170
+ */
2171
+ filter: object20().optional().default({}).label("Filters"),
2172
+ /**
2173
+ * How often to mail matches. `off` keeps the search saved but silent, which is
2174
+ * the difference between "I don't want mail" and "delete my search".
2175
+ *
2176
+ * Reuses the cadence enum from `marketingConsent.jobAlertFrequency` rather
2177
+ * than defining a second one: two vocabularies for the same concept is how
2178
+ * they drift.
2179
+ */
2180
+ frequency: string17().oneOf(SupportedJobAlertFrequencies).default("off" /* Off */).label("Alert Frequency"),
2181
+ status: string17().oneOf(SupportedSavedSearchStatuses).default("active" /* Active */).label("Status"),
2182
+ /**
2183
+ * Epoch ms of the last alert sent for this search. The matching sweep reads it
2184
+ * to decide whether the cadence is due, and writes it only AFTER a successful
2185
+ * publish, so a failed run retries next tick rather than silently skipping a
2186
+ * period.
2187
+ */
2188
+ lastNotifiedAt: number9().optional().label("Last Notified At"),
2189
+ /**
2190
+ * Epoch ms high-water mark: only jobs created after this are "new" for the
2191
+ * next alert. Kept separate from `lastNotifiedAt` because a run that finds no
2192
+ * matches should still advance what counts as new, without implying mail was
2193
+ * sent.
2194
+ */
2195
+ lastMatchedAt: number9().optional().label("Last Matched At")
2196
+ }).noUnknown().strict();
2197
+
2130
2198
  // src/report/report.schema.ts
2131
- import { mixed as mixed4, object as object20, string as string17 } from "yup";
2199
+ import { mixed as mixed4, object as object21, string as string18 } from "yup";
2132
2200
 
2133
2201
  // src/report/report.constant.ts
2134
2202
  var ResourceType = /* @__PURE__ */ ((ResourceType3) => {
@@ -2152,15 +2220,15 @@ var ReportReason = /* @__PURE__ */ ((ReportReason3) => {
2152
2220
  var SupportedReportReasons = Object.values(ReportReason);
2153
2221
 
2154
2222
  // src/report/report.schema.ts
2155
- var ReportSchema = object20({
2223
+ var ReportSchema = object21({
2156
2224
  type: mixed4().oneOf(SupportedResourceTypes).required().label("Type"),
2157
- resourceId: string17().required().label("Resource ID"),
2225
+ resourceId: string18().required().label("Resource ID"),
2158
2226
  reason: mixed4().oneOf(SupportedReportReasons).required("Please choose a reason").label("Reason"),
2159
- comment: string17().max(1e3).optional().label("Comment")
2227
+ comment: string18().max(1e3).optional().label("Comment")
2160
2228
  }).label("Report Schema");
2161
2229
 
2162
2230
  // src/feedback/feedback.schema.ts
2163
- import { mixed as mixed5, object as object21, string as string18 } from "yup";
2231
+ import { mixed as mixed5, object as object22, string as string19 } from "yup";
2164
2232
 
2165
2233
  // src/feedback/feedback.constant.ts
2166
2234
  var FeedbackType = /* @__PURE__ */ ((FeedbackType3) => {
@@ -2183,63 +2251,63 @@ var SupportedFeedbackStatuses = Object.values(FeedbackStatus);
2183
2251
  var FEEDBACK_MESSAGE_MAX = 4e3;
2184
2252
 
2185
2253
  // src/feedback/feedback.schema.ts
2186
- var FeedbackSchema = object21({
2254
+ var FeedbackSchema = object22({
2187
2255
  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"),
2256
+ message: string19().trim().required("Please enter a message").max(FEEDBACK_MESSAGE_MAX).label("Message"),
2257
+ email: string19().email().optional().label("Email"),
2258
+ pageUrl: string19().optional().label("Page URL"),
2191
2259
  status: mixed5().oneOf(SupportedFeedbackStatuses).optional().label("Status")
2192
2260
  }).label("Feedback Schema");
2193
2261
 
2194
2262
  // src/user/user.schema.ts
2195
- import { array as array14, boolean as boolean15, number as number10, object as object33, string as string31 } from "yup";
2263
+ import { array as array14, boolean as boolean15, number as number12, object as object34, string as string32 } from "yup";
2196
2264
 
2197
2265
  // src/user/work-experience.schema.ts
2198
- import { boolean as boolean12, object as object22, string as string19 } from "yup";
2199
- var WorkExperienceSchema = object22().shape({
2266
+ import { boolean as boolean12, object as object23, string as string20 } from "yup";
2267
+ var WorkExperienceSchema = object23().shape({
2200
2268
  company: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Company"),
2201
- designation: string19().trim().required().label("Designation"),
2269
+ designation: string20().trim().required().label("Designation"),
2202
2270
  duration: DurationSchema({
2203
2271
  format: "YYYY-MM",
2204
2272
  startLabel: "Start Date",
2205
2273
  endLabel: "End Date"
2206
2274
  }).required().label("Duration"),
2207
- description: string19().trim().max(3e3).optional().label("Description"),
2275
+ description: string20().trim().max(3e3).optional().label("Description"),
2208
2276
  location: LocationSchema.required().label("Location"),
2209
2277
  isRemote: boolean12().oneOf([true, false]).default(false).label("Is Remote")
2210
2278
  }).noUnknown().strict().label("Work Experience");
2211
2279
 
2212
2280
  // src/user/education.schema.ts
2213
- import { boolean as boolean13, object as object23, string as string20 } from "yup";
2214
- var EducationSchema = object23({
2281
+ import { boolean as boolean13, object as object24, string as string21 } from "yup";
2282
+ var EducationSchema = object24({
2215
2283
  institute: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Institute"),
2216
- course: string20().trim().required().label("Course"),
2217
- fieldOfStudy: string20().trim().required().label("Field of Study"),
2284
+ course: string21().trim().required().label("Course"),
2285
+ fieldOfStudy: string21().trim().required().label("Field of Study"),
2218
2286
  duration: DurationSchema({
2219
2287
  format: "YYYY-MM",
2220
2288
  startLabel: "Start Date",
2221
2289
  endLabel: "End Date"
2222
2290
  }).required().label("Duration"),
2223
- description: string20().trim().max(3e3).optional().label("Description"),
2291
+ description: string21().trim().max(3e3).optional().label("Description"),
2224
2292
  isDistanceLearning: boolean13().default(false).label("Is Distance Learning"),
2225
2293
  location: LocationSchema.required().label("Location"),
2226
- studyType: string20().oneOf(SupportedStudyTypes).trim().max(50).required().label("Study Type")
2294
+ studyType: string21().oneOf(SupportedStudyTypes).trim().max(50).required().label("Study Type")
2227
2295
  }).noUnknown().strict().label("Education");
2228
2296
 
2229
2297
  // src/user/user-skill.schema.ts
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"),
2298
+ import { object as object25, string as string22 } from "yup";
2299
+ var UserSkillSchema = object25({
2300
+ name: string22().required().label("Name"),
2301
+ proficiencyLevel: string22().oneOf(SupportedProficiencyLevels).required().label("Proficiency Level"),
2234
2302
  lastUsed: dateString().format("YYYY-MM").nullable().optional().label("Last Used")
2235
2303
  }).noUnknown().strict().label("Skill");
2236
2304
 
2237
2305
  // src/user/project.schema.ts
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"),
2306
+ import { object as object26, string as string23 } from "yup";
2307
+ var UserProjectSchema = object26({
2308
+ name: string23().trim().max(50).required().label("Name"),
2309
+ url: string23().optional().label("URL"),
2310
+ description: string23().trim().min(100).max(3e3).required().label("Description"),
2243
2311
  duration: DurationSchema({
2244
2312
  format: "YYYY-MM",
2245
2313
  startLabel: "Start Date",
@@ -2248,65 +2316,83 @@ var UserProjectSchema = object25({
2248
2316
  }).noUnknown().strict().label("Project");
2249
2317
 
2250
2318
  // src/user/user-certification.schema.ts
2251
- import { object as object26, string as string23 } from "yup";
2252
- var UserCertificationSchema = object26({
2253
- name: string23().trim().max(100).required().label("Name"),
2319
+ import { object as object27, string as string24 } from "yup";
2320
+ var UserCertificationSchema = object27({
2321
+ name: string24().trim().max(100).required().label("Name"),
2254
2322
  // TODO: Add validation for authority
2255
- authority: string23().trim().max(100).required().label("Authority"),
2256
- licenseNumber: string23().trim().max(50).optional().label("License Number"),
2323
+ authority: string24().trim().max(100).required().label("Authority"),
2324
+ licenseNumber: string24().trim().max(50).optional().label("License Number"),
2257
2325
  duration: DurationSchema({
2258
2326
  format: "YYYY-MM",
2259
2327
  startLabel: "Start Date",
2260
2328
  endLabel: "End Date"
2261
2329
  }).optional().nullable().label("Duration"),
2262
- url: string23().optional().label("URL")
2330
+ url: string24().optional().label("URL")
2263
2331
  }).noUnknown().strict().label("Certification");
2264
2332
 
2265
2333
  // src/user/user-interest.schema.ts
2266
- import { string as string24 } from "yup";
2267
- var UserInterestSchema = string24().trim().required().label("Interest");
2334
+ import { string as string25 } from "yup";
2335
+ var UserInterestSchema = string25().trim().required().label("Interest");
2268
2336
 
2269
2337
  // src/user/user-language.schema.ts
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")
2338
+ import { object as object28, string as string26 } from "yup";
2339
+ var UserLanguageSchema = object28({
2340
+ name: string26().trim().required().label("Name"),
2341
+ proficiencyLevel: string26().oneOf(SupportedProficiencyLevels).trim().max(50).required().label("Proficiency Level")
2274
2342
  }).noUnknown().strict().label("Language");
2275
2343
 
2276
2344
  // src/user/user-additional-info.schema.ts
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")
2345
+ import { object as object29, string as string27 } from "yup";
2346
+ var UserAdditionalInfoSchema = object29({
2347
+ title: string27().trim().max(60).required().label("Title"),
2348
+ description: string27().trim().max(1e3).required().label("Description")
2281
2349
  }).noUnknown().strict().label("User Additional Info");
2282
2350
 
2283
2351
  // src/user/general-detail.schema.ts
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")
2352
+ import { number as number10, object as object30, string as string28 } from "yup";
2353
+ var UserGeneralDetailSchema = object30({
2354
+ id: string28().optional().label("ID"),
2355
+ name: object30().shape({
2356
+ first: string28().trim().max(50).required().label("First Name"),
2357
+ last: string28().trim().max(50).optional().label("Last Name")
2290
2358
  }).required().label("Name"),
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"),
2359
+ headline: string28().trim().max(200).optional().label("Headline"),
2360
+ image: string28().optional().label("Image"),
2361
+ aboutMe: string28().trim().max(3e3).optional().label("About Me"),
2362
+ email: string28().required().label("Email"),
2363
+ mobile: string28().nullable().optional().label("Mobile"),
2364
+ emailVerified: string28().nullable().optional().label("Email Verified"),
2365
+ mobileVerified: string28().nullable().optional().label("Mobile Verified"),
2366
+ experienceLevel: string28().oneOf(SupportedExperienceLevels).required().label("Experience level"),
2299
2367
  location: LocationSchema.required().label("Location"),
2300
- region: object29().shape({
2301
- country: string27().trim().required().label("Region Country"),
2302
- lang: string27().trim().required().label("Region Language")
2303
- }).optional().default(void 0).label("Region"),
2304
- profileVisibility: string27().oneOf(SupportedUserProfileVisibilities).default("public" /* Public */).label("Profile Visibility")
2368
+ /**
2369
+ * Where the account was created, captured once and never rewritten.
2370
+ *
2371
+ * Server-owned: listed in user-service's SERVER_CONTROLLED_FIELDS and absent
2372
+ * from every user-writable schema, so a client cannot set or amend it. That
2373
+ * immutability is the point — this answers "which market/jurisdiction governed
2374
+ * this user at sign-up?", which a field the user can PATCH could never do.
2375
+ *
2376
+ * `source` records HOW the value was obtained, because most of it is inferred
2377
+ * rather than measured, and an audit has to be able to tell those apart:
2378
+ * - `cf-header` measured from Cloudflare's `cf-ipcountry` at sign-up
2379
+ * - `location` derived from the user's self-entered profile location
2380
+ * - `default` assumed (no signal available); presumption, not evidence
2381
+ */
2382
+ signupContext: object30().shape({
2383
+ // ISO 3166-1 alpha-2, uppercase — the shape `cf-ipcountry` returns, so
2384
+ // measured and inferred values are directly comparable.
2385
+ country: string28().trim().uppercase().length(2).required().label("Signup Country"),
2386
+ locale: string28().trim().required().label("Signup Locale"),
2387
+ source: string28().oneOf(SupportedSignupContextSources).required().label("Signup Context Source"),
2388
+ at: number10().required().label("Signup Context At")
2389
+ }).optional().default(void 0).label("Signup Context"),
2390
+ profileVisibility: string28().oneOf(SupportedUserProfileVisibilities).default("public" /* Public */).label("Profile Visibility")
2305
2391
  }).noUnknown().strict().label("General Detail");
2306
2392
 
2307
2393
  // src/user/user-job-preferences.schema.ts
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({
2394
+ import { array as array11, boolean as boolean14, date as date2, number as number11, object as object31, string as string29 } from "yup";
2395
+ var UserJobPreferencesSchema = object31({
2310
2396
  /**
2311
2397
  * Whether the job seeker is openly signalling availability. Surfaces the
2312
2398
  * "Open to work" badge on the public profile and boosts visibility in
@@ -2318,7 +2404,7 @@ var UserJobPreferencesSchema = object30({
2318
2404
  * and can downgrade the visibility of expired or low-relevance postings for
2319
2405
  * users in `passively_browsing` mode.
2320
2406
  */
2321
- jobSearchUrgency: string28().oneOf(SupportedJobSearchUrgencies).required().label("Job Search Urgency"),
2407
+ jobSearchUrgency: string29().oneOf(SupportedJobSearchUrgencies).required().label("Job Search Urgency"),
2322
2408
  /**
2323
2409
  * Locations the user wants to work in. Stores the full `LocationSchema`
2324
2410
  * shape (country, city, state, geo, etc.) as returned by the locations
@@ -2335,7 +2421,7 @@ var UserJobPreferencesSchema = object30({
2335
2421
  * Both reuse the same `ExperienceLevel` taxonomy from `common.constant.ts`
2336
2422
  * so search/match logic doesn't have to translate between two vocabularies.
2337
2423
  */
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"),
2424
+ targetExperienceLevels: array11().of(string29().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"),
2339
2425
  /**
2340
2426
  * Specializations the user is searching for - free-form titles.
2341
2427
  *
@@ -2344,19 +2430,19 @@ var UserJobPreferencesSchema = object30({
2344
2430
  * surfaces a curated "Popular Roles" picker for discovery, but the user
2345
2431
  * can ultimately add any title; matching/normalization happens downstream.
2346
2432
  */
2347
- jobRoles: array11().of(string28().trim().max(120).required()).min(1, "Pick at least one role.").required().label("Job Roles"),
2433
+ jobRoles: array11().of(string29().trim().max(120).required()).min(1, "Pick at least one role.").required().label("Job Roles"),
2348
2434
  /**
2349
2435
  * Minimum acceptable annual base salary, in `minSalaryCurrency`.
2350
2436
  * Stored as an integer in the currency's major unit (e.g. dollars, not cents).
2351
2437
  * Defaults to 0 ("any salary") rather than being optional - every job-seeker
2352
2438
  * has a floor, even if it's zero.
2353
2439
  */
2354
- minSalary: number9().integer().min(MIN_SALARY_LOWER_BOUND).max(MIN_SALARY_UPPER_BOUND).default(0).required().label("Minimum Salary"),
2355
- minSalaryCurrency: string28().oneOf(SupportedSalaryCurrencies).default("USD").required().label("Minimum Salary Currency"),
2440
+ minSalary: number11().integer().min(MIN_SALARY_LOWER_BOUND).max(MIN_SALARY_UPPER_BOUND).default(0).required().label("Minimum Salary"),
2441
+ minSalaryCurrency: string29().oneOf(SupportedSalaryCurrencies).default("USD").required().label("Minimum Salary Currency"),
2356
2442
  /**
2357
2443
  * Marketing-attribution capture from the final onboarding step.
2358
2444
  */
2359
- referralSource: string28().oneOf(SupportedReferralSources).required().label("Referral Source"),
2445
+ referralSource: string29().oneOf(SupportedReferralSources).required().label("Referral Source"),
2360
2446
  /**
2361
2447
  * Resumes the user has uploaded. Embedded directly on the user document
2362
2448
  * (capped at 5) - they're cheap to denormalize, every wizard step that
@@ -2371,15 +2457,15 @@ var UserJobPreferencesSchema = object30({
2371
2457
  * live in their own collection.
2372
2458
  */
2373
2459
  resumes: array11().of(
2374
- object30({
2460
+ object31({
2375
2461
  // Stable id assigned on upload - used to address a specific entry
2376
2462
  // for delete / set-primary operations without exposing the GCS URL
2377
2463
  // as a route key.
2378
- id: string28().required().label("ID"),
2379
- url: string28().required().label("Resume URL"),
2380
- filename: string28().trim().max(255).optional().label("Filename"),
2381
- sizeBytes: number9().integer().min(0).optional().label("Size (bytes)"),
2382
- mimeType: string28().trim().max(120).optional().label("MIME Type"),
2464
+ id: string29().required().label("ID"),
2465
+ url: string29().required().label("Resume URL"),
2466
+ filename: string29().trim().max(255).optional().label("Filename"),
2467
+ sizeBytes: number11().integer().min(0).optional().label("Size (bytes)"),
2468
+ mimeType: string29().trim().max(120).optional().label("MIME Type"),
2383
2469
  uploadedAt: date2().required().label("Uploaded At"),
2384
2470
  isPrimary: boolean14().default(false).label("Is Primary")
2385
2471
  }).noUnknown().strict().label("Resume")
@@ -2408,12 +2494,12 @@ var UserJobPreferencesSchema = object30({
2408
2494
  * APIs. If `user.<field>` is empty there, the correct behavior is empty,
2409
2495
  * because the user has not confirmed it yet.
2410
2496
  */
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"),
2497
+ pendingPrefill: object31({
2498
+ headline: string29().trim().optional().label("Pending Headline"),
2499
+ aboutMe: string29().trim().optional().label("Pending About Me"),
2500
+ mobile: string29().trim().optional().label("Pending Mobile"),
2415
2501
  location: LocationSchema.optional().default(void 0).label("Pending Location"),
2416
- experienceLevel: string28().oneOf(SupportedExperienceLevels).optional().label("Pending Experience Level"),
2502
+ experienceLevel: string29().oneOf(SupportedExperienceLevels).optional().label("Pending Experience Level"),
2417
2503
  socialAccounts: array11().of(SocialAccountSchema.required()).optional().label("Pending Social Accounts"),
2418
2504
  workExperiences: array11().of(WorkExperienceSchema.required()).optional().label("Pending Work Experiences"),
2419
2505
  educations: array11().of(EducationSchema.required()).optional().label("Pending Educations")
@@ -2421,35 +2507,35 @@ var UserJobPreferencesSchema = object30({
2421
2507
  }).noUnknown().strict().label("User Job Preferences Schema");
2422
2508
 
2423
2509
  // src/user/user-recruiter-profile.schema.ts
2424
- import { array as array12, object as object31, string as string29 } from "yup";
2425
- var RecruiterPageLinkSchema = object31({
2510
+ import { array as array12, object as object32, string as string30 } from "yup";
2511
+ var RecruiterPageLinkSchema = object32({
2426
2512
  page: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Company Page"),
2427
- jobTitle: string29().trim().max(100).optional().label("Job Title")
2513
+ jobTitle: string30().trim().max(100).optional().label("Job Title")
2428
2514
  }).noUnknown().strict().label("Recruiter Page Link");
2429
- var UserRecruiterProfileSchema = object31({
2430
- recruiterProfile: object31({
2515
+ var UserRecruiterProfileSchema = object32({
2516
+ recruiterProfile: object32({
2431
2517
  hiringFor: array12().of(RecruiterPageLinkSchema).default([]).label("Hiring For")
2432
2518
  }).optional().default(void 0).label("Recruiter Profile")
2433
2519
  }).noUnknown().strict().label("User Recruiter Profile");
2434
2520
 
2435
2521
  // src/user/user-coordinator-profile.schema.ts
2436
- import { array as array13, object as object32, string as string30 } from "yup";
2437
- var CoordinatorPageLinkSchema = object32({
2522
+ import { array as array13, object as object33, string as string31 } from "yup";
2523
+ var CoordinatorPageLinkSchema = object33({
2438
2524
  page: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Institute Page"),
2439
- jobTitle: string30().trim().max(100).optional().label("Job Title")
2525
+ jobTitle: string31().trim().max(100).optional().label("Job Title")
2440
2526
  }).noUnknown().strict().label("Coordinator Page Link");
2441
- var UserCoordinatorProfileSchema = object32({
2442
- coordinatorProfile: object32({
2527
+ var UserCoordinatorProfileSchema = object33({
2528
+ coordinatorProfile: object33({
2443
2529
  coordinatesAt: array13().of(CoordinatorPageLinkSchema).default([]).label("Coordinates At")
2444
2530
  }).optional().default(void 0).label("Coordinator Profile")
2445
2531
  }).noUnknown().strict().label("User Coordinator Profile");
2446
2532
 
2447
2533
  // src/user/user.schema.ts
2448
- var UserSchema = object33({
2534
+ var UserSchema = object34({
2449
2535
  /**
2450
2536
  * Related auth account id from auth Server (e.g. Better auth https://auth.thejob.dev)
2451
2537
  */
2452
- authAccountId: string31().required().label("Auth Account ID"),
2538
+ authAccountId: string32().required().label("Auth Account ID"),
2453
2539
  /**
2454
2540
  * Social media information about the user (e.g. LinkedIn, GitHub, etc.)
2455
2541
  */
@@ -2489,11 +2575,11 @@ var UserSchema = object33({
2489
2575
  /**
2490
2576
  * Status of the user account
2491
2577
  */
2492
- status: string31().oneOf(SupportedUserStatuses).required().label("Status"),
2578
+ status: string32().oneOf(SupportedUserStatuses).required().label("Status"),
2493
2579
  /**
2494
2580
  * Roles assigned to the user
2495
2581
  */
2496
- roles: array14().of(string31().oneOf(SupportedUserRoles)).required().default(DefaultUserRoles).label("Roles"),
2582
+ roles: array14().of(string32().oneOf(SupportedUserRoles)).required().default(DefaultUserRoles).label("Roles"),
2497
2583
  /**
2498
2584
  * Explicit consent to receive marketing email (newsletters, job alerts by
2499
2585
  * broadcast, product updates). Marketing sends are HARD-GATED on
@@ -2505,18 +2591,18 @@ var UserSchema = object33({
2505
2591
  * unsubscribe sets `subscribed=false` so consent and the email-service
2506
2592
  * suppression list stay consistent. Defaults to NOT subscribed (opt-in).
2507
2593
  */
2508
- marketingConsent: object33({
2594
+ marketingConsent: object34({
2509
2595
  subscribed: boolean15().default(false).label("Subscribed to marketing email"),
2510
2596
  /** Epoch ms of the last consent change (opt-in or opt-out). */
2511
- updatedAt: number10().optional().label("Consent updated at"),
2597
+ updatedAt: number12().optional().label("Consent updated at"),
2512
2598
  /** Where the consent change originated, e.g. `onboarding`, `unsubscribe`, `settings`. */
2513
- source: string31().optional().label("Consent source"),
2599
+ source: string32().optional().label("Consent source"),
2514
2600
  /**
2515
2601
  * How often the user receives job-alert emails. `off` disables job alerts
2516
2602
  * only; other marketing stays governed by `subscribed`. Unset means the
2517
2603
  * user has not chosen a cadence yet.
2518
2604
  */
2519
- jobAlertFrequency: string31().oneOf(SupportedJobAlertFrequencies).optional().label("Job alert frequency")
2605
+ jobAlertFrequency: string32().oneOf(SupportedJobAlertFrequencies).optional().label("Job alert frequency")
2520
2606
  }).default({ subscribed: false }).label("Marketing Consent"),
2521
2607
  /**
2522
2608
  * Per-channel toggles for NOTIFICATION email — mail about activity on the
@@ -2531,7 +2617,7 @@ var UserSchema = object33({
2531
2617
  *
2532
2618
  * Transactional mail (magic-link, verification) ignores this object entirely.
2533
2619
  */
2534
- notificationPrefs: object33({
2620
+ notificationPrefs: object34({
2535
2621
  groupActivity: boolean15().default(true).label("Group activity email"),
2536
2622
  jobActivity: boolean15().default(true).label("Job activity email"),
2537
2623
  applicationUpdates: boolean15().default(true).label("Application update email"),
@@ -2547,15 +2633,15 @@ var UserSchema = object33({
2547
2633
  * Generated from a structured summary of skills, experience, education, etc.
2548
2634
  * Only generated for public profiles with sufficient completeness (≥60%).
2549
2635
  */
2550
- embedding: object33({
2551
- vector: array14(number10().required()).optional().label("Embedding vector"),
2552
- model: string31().optional().label("Embedding model")
2636
+ embedding: object34({
2637
+ vector: array14(number12().required()).optional().label("Embedding vector"),
2638
+ model: string32().optional().label("Embedding model")
2553
2639
  }).nullable().optional().label("Embedding")
2554
2640
  }).concat(UserGeneralDetailSchema).concat(UserJobPreferencesSchema).concat(UserRecruiterProfileSchema).concat(UserCoordinatorProfileSchema).noUnknown().strict().label("User Schema");
2555
2641
 
2556
2642
  // src/user/user-completeness.schema.ts
2557
- import { object as object34 } from "yup";
2558
- var UserCompletenessSchema = object34({
2643
+ import { object as object35 } from "yup";
2644
+ var UserCompletenessSchema = object35({
2559
2645
  additionalInfo: CompletenessScoreSchema(0).label("Additional Info"),
2560
2646
  // Optional
2561
2647
  certifications: CompletenessScoreSchema(0).label("Certifications"),
@@ -2647,6 +2733,9 @@ export {
2647
2733
  ResourceType,
2648
2734
  SITEMAP_FORMAT,
2649
2735
  SYSTEM_DATE_FORMAT,
2736
+ SavedSearchSchema,
2737
+ SavedSearchStatus,
2738
+ SignupContextSource,
2650
2739
  SkillSchema,
2651
2740
  SocialAccount,
2652
2741
  SocialAccountSchema,
@@ -2679,6 +2768,8 @@ export {
2679
2768
  SupportedReportReasons,
2680
2769
  SupportedResourceTypes,
2681
2770
  SupportedSalaryCurrencies,
2771
+ SupportedSavedSearchStatuses,
2772
+ SupportedSignupContextSources,
2682
2773
  SupportedSocialAccounts,
2683
2774
  SupportedStudyTypes,
2684
2775
  SupportedUserProfileVisibilities,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thejob/schema",
3
- "version": "2.1.3",
3
+ "version": "2.1.5",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
package/src/index.ts CHANGED
@@ -62,6 +62,12 @@ export * from "./marketing/marketing.constant.js";
62
62
  export * from "./pagination/pagination.schema.js";
63
63
  export * from "./pagination/pagination.constant.js";
64
64
 
65
+ /**
66
+ * Saved-search schemas and constants (job alerts).
67
+ */
68
+ export * from "./saved-search/saved-search.schema.js";
69
+ export * from "./saved-search/saved-search.constant.js";
70
+
65
71
  /**
66
72
  * Report schemas and constants.
67
73
  */
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Lifecycle of a saved search. Lowercase values, like every other enum in this
3
+ * package: the string is stored in Mongo and compared as a literal across
4
+ * services, so its casing is data rather than style.
5
+ */
6
+ export enum SavedSearchStatus {
7
+ /** Matching runs and alerts may be sent. */
8
+ Active = "active",
9
+ /**
10
+ * Kept for the user to see and re-enable, but skipped by the matching sweep.
11
+ * Distinct from deleting: a user pausing alerts should not lose the query they
12
+ * spent time building.
13
+ */
14
+ Paused = "paused",
15
+ }
16
+
17
+ export const SupportedSavedSearchStatuses = Object.values(SavedSearchStatus);