@thejob/schema 2.1.9 → 2.3.0
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 +17 -0
- package/dist/index.cjs +2178 -723
- package/dist/index.d.cts +1991 -535
- package/dist/index.d.ts +1991 -535
- package/dist/index.js +2080 -698
- package/package.json +1 -1
- package/src/billing/billing.constant.ts +179 -0
- package/src/billing/billing.schema.ts +280 -0
- package/src/common/facet.schema.ts +342 -0
- package/src/feedback/feedback-filter.schema.ts +39 -0
- package/src/geo/city-filter.schema.ts +41 -0
- package/src/geo/city.schema.ts +104 -0
- package/src/geo/country-filter.schema.ts +47 -0
- package/src/geo/country.schema.ts +148 -0
- package/src/geo/geo-sync.schema.ts +116 -0
- package/src/geo/geo.constant.ts +78 -0
- package/src/geo/state-filter.schema.ts +57 -0
- package/src/geo/state.schema.ts +101 -0
- package/src/group/group-filter.schema.ts +81 -0
- package/src/group/group.constant.ts +15 -0
- package/src/index.ts +35 -0
- package/src/job/job-filter.schema.ts +266 -0
- package/src/language/language-filter.schema.ts +40 -0
- package/src/marketing/marketing.constant.ts +105 -30
- package/src/marketing/marketing.schema.ts +118 -403
- package/src/page/page-filter.schema.ts +67 -0
- package/src/skill/skill-filter.schema.ts +61 -0
- package/src/user/push-subscription.schema.ts +97 -0
- package/src/user/user-filter.schema.ts +438 -0
- package/src/user/user-interest.schema.ts +34 -2
- package/src/user/user.constant.ts +20 -8
- package/src/user/user.schema.ts +45 -0
- package/src/user/user.types.ts +35 -0
package/dist/index.cjs
CHANGED
|
@@ -32,22 +32,29 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
AnswerChoiceType: () => AnswerChoiceType,
|
|
34
34
|
ApplicationReceivePreference: () => ApplicationReceivePreference,
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
BillingPeriod: () => BillingPeriod,
|
|
36
|
+
BillingSubscriptionSchema: () => BillingSubscriptionSchema,
|
|
37
|
+
BillingTransactionSchema: () => BillingTransactionSchema,
|
|
37
38
|
CAMPAIGN_MAX_RECIPIENTS_CEILING: () => CAMPAIGN_MAX_RECIPIENTS_CEILING,
|
|
38
|
-
|
|
39
|
+
CONSUME_MAX_UNITS: () => CONSUME_MAX_UNITS,
|
|
40
|
+
CampaignDataSourceSchema: () => CampaignDataSourceSchema,
|
|
39
41
|
CampaignEndKind: () => CampaignEndKind,
|
|
40
42
|
CampaignIntervalUnit: () => CampaignIntervalUnit,
|
|
41
43
|
CampaignSchema: () => CampaignSchema,
|
|
42
44
|
CampaignStatus: () => CampaignStatus,
|
|
45
|
+
CampaignStatusTransitions: () => CampaignStatusTransitions,
|
|
43
46
|
CampaignTriggerKind: () => CampaignTriggerKind,
|
|
44
47
|
CampaignTriggerSchema: () => CampaignTriggerSchema,
|
|
45
48
|
ChoiceQuestionSchema: () => ChoiceQuestionSchema,
|
|
49
|
+
CityFilterSchema: () => CityFilterSchema,
|
|
50
|
+
CitySchema: () => CitySchema,
|
|
46
51
|
Common: () => Common,
|
|
47
52
|
CompensationType: () => CompensationType,
|
|
48
53
|
CompletenessScoreSchema: () => CompletenessScoreSchema,
|
|
49
54
|
ContactTypes: () => ContactTypes,
|
|
50
55
|
CoordinatorPageLinkSchema: () => CoordinatorPageLinkSchema,
|
|
56
|
+
CountryFilterSchema: () => CountryFilterSchema,
|
|
57
|
+
CountrySchema: () => CountrySchema,
|
|
51
58
|
DISPLAY_DATE_FORMAT: () => DISPLAY_DATE_FORMAT,
|
|
52
59
|
DISPLAY_DATE_FORMAT_SHORT: () => DISPLAY_DATE_FORMAT_SHORT,
|
|
53
60
|
DateStringSchema: () => DateStringSchema,
|
|
@@ -61,12 +68,29 @@ __export(index_exports, {
|
|
|
61
68
|
EducationSchema: () => EducationSchema,
|
|
62
69
|
EmployeeCount: () => EmployeeCount,
|
|
63
70
|
EmploymentType: () => EmploymentType,
|
|
71
|
+
EntitlementConsumeSchema: () => EntitlementConsumeSchema,
|
|
72
|
+
EntitlementDenialReason: () => EntitlementDenialReason,
|
|
73
|
+
EntitlementDenialSchema: () => EntitlementDenialSchema,
|
|
74
|
+
EntitlingSubscriptionStatuses: () => EntitlingSubscriptionStatuses,
|
|
64
75
|
ExperienceLevel: () => ExperienceLevel,
|
|
65
76
|
FEEDBACK_MESSAGE_MAX: () => FEEDBACK_MESSAGE_MAX,
|
|
77
|
+
FeatureEntitlementSchema: () => FeatureEntitlementSchema,
|
|
78
|
+
FeatureKey: () => FeatureKey,
|
|
79
|
+
FeedbackFilterSchema: () => FeedbackFilterSchema,
|
|
66
80
|
FeedbackSchema: () => FeedbackSchema,
|
|
67
81
|
FeedbackStatus: () => FeedbackStatus,
|
|
68
82
|
FeedbackType: () => FeedbackType,
|
|
83
|
+
GEO_COLLECTIONS: () => GEO_COLLECTIONS,
|
|
84
|
+
GEO_SEARCH_MAX: () => GEO_SEARCH_MAX,
|
|
85
|
+
GRANT_MAX_LIMIT: () => GRANT_MAX_LIMIT,
|
|
69
86
|
GeneraDetailFields: () => GeneraDetailFields,
|
|
87
|
+
GeoKind: () => GeoKind,
|
|
88
|
+
GeoSyncPhase: () => GeoSyncPhase,
|
|
89
|
+
GeoSyncRunSchema: () => GeoSyncRunSchema,
|
|
90
|
+
GeoSyncStatus: () => GeoSyncStatus,
|
|
91
|
+
GeoSyncTrigger: () => GeoSyncTrigger,
|
|
92
|
+
GrantType: () => GrantType,
|
|
93
|
+
GroupFilterSchema: () => GroupFilterSchema,
|
|
70
94
|
GroupManagedBy: () => GroupManagedBy,
|
|
71
95
|
GroupMembershipSchema: () => GroupMembershipSchema,
|
|
72
96
|
GroupMembershipStatus: () => GroupMembershipStatus,
|
|
@@ -78,31 +102,41 @@ __export(index_exports, {
|
|
|
78
102
|
JOB_TAXONOMY: () => JOB_TAXONOMY,
|
|
79
103
|
JobAlertFrequency: () => JobAlertFrequency,
|
|
80
104
|
JobCategory: () => JobCategory,
|
|
105
|
+
JobFilterSchema: () => JobFilterSchema,
|
|
81
106
|
JobIndustry: () => JobIndustry,
|
|
82
107
|
JobSchema: () => JobSchema,
|
|
83
108
|
JobSearchUrgency: () => JobSearchUrgency,
|
|
84
109
|
JobStatus: () => JobStatus,
|
|
85
110
|
JobSubCategory: () => JobSubCategory,
|
|
86
|
-
|
|
87
|
-
ListFilterBranchSchema: () => ListFilterBranchSchema,
|
|
88
|
-
ListFilterSchema: () => ListFilterSchema,
|
|
111
|
+
LanguageFilterSchema: () => LanguageFilterSchema,
|
|
89
112
|
LocationSchema: () => LocationSchema,
|
|
90
113
|
MIN_SALARY_LOWER_BOUND: () => MIN_SALARY_LOWER_BOUND,
|
|
91
114
|
MIN_SALARY_UPPER_BOUND: () => MIN_SALARY_UPPER_BOUND,
|
|
115
|
+
MailingListFilterSchema: () => MailingListFilterSchema,
|
|
92
116
|
MailingListSchema: () => MailingListSchema,
|
|
93
|
-
|
|
117
|
+
MoneySchema: () => MoneySchema,
|
|
118
|
+
NotificationType: () => NotificationType,
|
|
94
119
|
OccurrenceStatus: () => OccurrenceStatus,
|
|
95
120
|
PREFILL_MIRRORED_KEYS: () => PREFILL_MIRRORED_KEYS,
|
|
96
121
|
PRIVATE_PROFILE_FIELDS: () => PRIVATE_PROFILE_FIELDS,
|
|
122
|
+
PaddleEnvironment: () => PaddleEnvironment,
|
|
123
|
+
PaddlePriceIdsSchema: () => PaddlePriceIdsSchema,
|
|
124
|
+
PageFilterSchema: () => PageFilterSchema,
|
|
97
125
|
PageSchema: () => PageSchema,
|
|
98
126
|
PageStatus: () => PageStatus,
|
|
99
127
|
PageType: () => PageType,
|
|
100
128
|
PaginationSchema: () => PaginationSchema,
|
|
129
|
+
PlanGrantSchema: () => PlanGrantSchema,
|
|
130
|
+
PlanKind: () => PlanKind,
|
|
131
|
+
PlanSchema: () => PlanSchema,
|
|
132
|
+
PlanStatus: () => PlanStatus,
|
|
101
133
|
PostSchema: () => PostSchema,
|
|
102
134
|
PostStatus: () => PostStatus,
|
|
103
135
|
ProficiencyLevel: () => ProficiencyLevel,
|
|
136
|
+
PushSubscriptionSchema: () => PushSubscriptionSchema,
|
|
104
137
|
QuestionSchema: () => QuestionSchema,
|
|
105
138
|
QuestionType: () => QuestionType,
|
|
139
|
+
QuotaPeriod: () => QuotaPeriod,
|
|
106
140
|
ReadAndAcknowledgeQuestionSchema: () => ReadAndAcknowledgeQuestionSchema,
|
|
107
141
|
RecruiterPageLinkSchema: () => RecruiterPageLinkSchema,
|
|
108
142
|
ReferralSource: () => ReferralSource,
|
|
@@ -114,15 +148,18 @@ __export(index_exports, {
|
|
|
114
148
|
SavedSearchSchema: () => SavedSearchSchema,
|
|
115
149
|
SavedSearchStatus: () => SavedSearchStatus,
|
|
116
150
|
SignupContextSource: () => SignupContextSource,
|
|
151
|
+
SkillFilterSchema: () => SkillFilterSchema,
|
|
117
152
|
SkillSchema: () => SkillSchema,
|
|
118
153
|
SocialAccount: () => SocialAccount,
|
|
119
154
|
SocialAccountSchema: () => SocialAccountSchema,
|
|
155
|
+
StateFilterSchema: () => StateFilterSchema,
|
|
156
|
+
StateSchema: () => StateSchema,
|
|
120
157
|
StudentCompletenessSchema: () => StudentCompletenessSchema,
|
|
121
158
|
StudyType: () => StudyType,
|
|
159
|
+
SubscriptionStatus: () => SubscriptionStatus,
|
|
122
160
|
SupportedAnswerChoiceTypes: () => SupportedAnswerChoiceTypes,
|
|
123
161
|
SupportedApplicationReceivePreferences: () => SupportedApplicationReceivePreferences,
|
|
124
|
-
|
|
125
|
-
SupportedCampaignAnchorFields: () => SupportedCampaignAnchorFields,
|
|
162
|
+
SupportedBillingPeriods: () => SupportedBillingPeriods,
|
|
126
163
|
SupportedCampaignEndKinds: () => SupportedCampaignEndKinds,
|
|
127
164
|
SupportedCampaignIntervalUnits: () => SupportedCampaignIntervalUnits,
|
|
128
165
|
SupportedCampaignStatuses: () => SupportedCampaignStatuses,
|
|
@@ -132,22 +169,37 @@ __export(index_exports, {
|
|
|
132
169
|
SupportedEducationLevels: () => SupportedEducationLevels,
|
|
133
170
|
SupportedEmployeeCounts: () => SupportedEmployeeCounts,
|
|
134
171
|
SupportedEmploymentTypes: () => SupportedEmploymentTypes,
|
|
172
|
+
SupportedEntitlementDenialReasons: () => SupportedEntitlementDenialReasons,
|
|
135
173
|
SupportedExperienceLevels: () => SupportedExperienceLevels,
|
|
174
|
+
SupportedFeatureKeys: () => SupportedFeatureKeys,
|
|
136
175
|
SupportedFeedbackStatuses: () => SupportedFeedbackStatuses,
|
|
137
176
|
SupportedFeedbackTypes: () => SupportedFeedbackTypes,
|
|
177
|
+
SupportedGeoKinds: () => SupportedGeoKinds,
|
|
178
|
+
SupportedGeoSyncPhases: () => SupportedGeoSyncPhases,
|
|
179
|
+
SupportedGeoSyncStatuses: () => SupportedGeoSyncStatuses,
|
|
180
|
+
SupportedGeoSyncTriggers: () => SupportedGeoSyncTriggers,
|
|
181
|
+
SupportedGrantTypes: () => SupportedGrantTypes,
|
|
182
|
+
SupportedGroupManagedBy: () => SupportedGroupManagedBy,
|
|
183
|
+
SupportedGroupMembershipStatuses: () => SupportedGroupMembershipStatuses,
|
|
184
|
+
SupportedGroupStatuses: () => SupportedGroupStatuses,
|
|
185
|
+
SupportedGroupVisibilities: () => SupportedGroupVisibilities,
|
|
138
186
|
SupportedJobAlertFrequencies: () => SupportedJobAlertFrequencies,
|
|
139
187
|
SupportedJobCategories: () => SupportedJobCategories,
|
|
140
188
|
SupportedJobIndustries: () => SupportedJobIndustries,
|
|
141
189
|
SupportedJobSearchUrgencies: () => SupportedJobSearchUrgencies,
|
|
142
190
|
SupportedJobStatuses: () => SupportedJobStatuses,
|
|
143
191
|
SupportedJobSubCategories: () => SupportedJobSubCategories,
|
|
144
|
-
|
|
192
|
+
SupportedNotificationTypes: () => SupportedNotificationTypes,
|
|
145
193
|
SupportedOccurrenceStatuses: () => SupportedOccurrenceStatuses,
|
|
194
|
+
SupportedPaddleEnvironments: () => SupportedPaddleEnvironments,
|
|
146
195
|
SupportedPageStatuses: () => SupportedPageStatuses,
|
|
147
196
|
SupportedPageTypes: () => SupportedPageTypes,
|
|
197
|
+
SupportedPlanKinds: () => SupportedPlanKinds,
|
|
198
|
+
SupportedPlanStatuses: () => SupportedPlanStatuses,
|
|
148
199
|
SupportedPostStatuses: () => SupportedPostStatuses,
|
|
149
200
|
SupportedProficiencyLevels: () => SupportedProficiencyLevels,
|
|
150
201
|
SupportedQuestionTypes: () => SupportedQuestionTypes,
|
|
202
|
+
SupportedQuotaPeriods: () => SupportedQuotaPeriods,
|
|
151
203
|
SupportedReferralSources: () => SupportedReferralSources,
|
|
152
204
|
SupportedReportReasons: () => SupportedReportReasons,
|
|
153
205
|
SupportedResourceTypes: () => SupportedResourceTypes,
|
|
@@ -156,17 +208,26 @@ __export(index_exports, {
|
|
|
156
208
|
SupportedSignupContextSources: () => SupportedSignupContextSources,
|
|
157
209
|
SupportedSocialAccounts: () => SupportedSocialAccounts,
|
|
158
210
|
SupportedStudyTypes: () => SupportedStudyTypes,
|
|
211
|
+
SupportedSubscriptionStatuses: () => SupportedSubscriptionStatuses,
|
|
212
|
+
SupportedTemplateVariableBindingKinds: () => SupportedTemplateVariableBindingKinds,
|
|
213
|
+
SupportedTransactionStatuses: () => SupportedTransactionStatuses,
|
|
159
214
|
SupportedUserProfileVisibilities: () => SupportedUserProfileVisibilities,
|
|
160
215
|
SupportedUserRoles: () => SupportedUserRoles,
|
|
161
216
|
SupportedUserStatuses: () => SupportedUserStatuses,
|
|
162
217
|
SupportedWorkModes: () => SupportedWorkModes,
|
|
163
218
|
TAXONOMY_LABELS: () => TAXONOMY_LABELS,
|
|
219
|
+
TemplateVariableBindingKind: () => TemplateVariableBindingKind,
|
|
220
|
+
TemplateVariableBindingSchema: () => TemplateVariableBindingSchema,
|
|
221
|
+
TerminalCampaignStatuses: () => TerminalCampaignStatuses,
|
|
222
|
+
TerminalGeoSyncStatuses: () => TerminalGeoSyncStatuses,
|
|
164
223
|
TermsAcceptedSchema: () => TermsAcceptedSchema,
|
|
224
|
+
TransactionStatus: () => TransactionStatus,
|
|
165
225
|
UserAdditionalInfoSchema: () => UserAdditionalInfoSchema,
|
|
166
226
|
UserCertificationSchema: () => UserCertificationSchema,
|
|
167
227
|
UserCompletenessSchema: () => UserCompletenessSchema,
|
|
168
228
|
UserCoordinatorProfileSchema: () => UserCoordinatorProfileSchema,
|
|
169
229
|
UserDetailType: () => UserDetailType,
|
|
230
|
+
UserFilterSchema: () => UserFilterSchema,
|
|
170
231
|
UserGeneralDetailSchema: () => UserGeneralDetailSchema,
|
|
171
232
|
UserIdAndCreatedAtSchema: () => UserIdAndCreatedAtSchema,
|
|
172
233
|
UserInterestSchema: () => UserInterestSchema,
|
|
@@ -181,14 +242,26 @@ __export(index_exports, {
|
|
|
181
242
|
UserStatus: () => UserStatus,
|
|
182
243
|
WorkExperienceSchema: () => WorkExperienceSchema,
|
|
183
244
|
WorkMode: () => WorkMode,
|
|
245
|
+
canTransitionCampaign: () => canTransitionCampaign,
|
|
184
246
|
categoryOfIndustry: () => categoryOfIndustry,
|
|
185
247
|
categoryOfSubCategory: () => categoryOfSubCategory,
|
|
248
|
+
codeTokens: () => codeTokens,
|
|
249
|
+
conjunction: () => conjunction,
|
|
250
|
+
currencyBound: () => currencyBound,
|
|
186
251
|
dateString: () => dateString,
|
|
252
|
+
days: () => days,
|
|
187
253
|
deriveIndustry: () => deriveIndustry,
|
|
254
|
+
enumFacet: () => enumFacet,
|
|
255
|
+
epoch: () => epoch,
|
|
188
256
|
getSchemaByQuestion: () => getSchemaByQuestion,
|
|
189
257
|
industriesOfCategory: () => industriesOfCategory,
|
|
190
258
|
industryOfSubCategory: () => industryOfSubCategory,
|
|
191
|
-
|
|
259
|
+
months: () => months,
|
|
260
|
+
percent: () => percent,
|
|
261
|
+
qualified: () => qualified,
|
|
262
|
+
rejectUnknownKeys: () => rejectUnknownKeys,
|
|
263
|
+
subCategoriesOfIndustry: () => subCategoriesOfIndustry,
|
|
264
|
+
tokens: () => tokens
|
|
192
265
|
});
|
|
193
266
|
module.exports = __toCommonJS(index_exports);
|
|
194
267
|
|
|
@@ -214,6 +287,102 @@ var UserIdAndCreatedAtSchema = (0, import_yup.object)().shape({
|
|
|
214
287
|
createdAt: (0, import_yup.string)().required().label("Created At")
|
|
215
288
|
});
|
|
216
289
|
|
|
290
|
+
// src/common/facet.schema.ts
|
|
291
|
+
var import_yup2 = require("yup");
|
|
292
|
+
var tokens = (label) => (0, import_yup2.array)().of((0, import_yup2.string)().trim().required()).optional().default([]).label(label);
|
|
293
|
+
var codeTokens = (label) => (0, import_yup2.array)().of((0, import_yup2.string)().trim().uppercase().required()).optional().default([]).label(label);
|
|
294
|
+
var enumFacet = (values, label) => (0, import_yup2.array)().of((0, import_yup2.string)().oneOf(values).required()).optional().default([]).label(label);
|
|
295
|
+
var days = (label) => (0, import_yup2.number)().integer().min(0).max(3650).optional().default(void 0).label(label);
|
|
296
|
+
var months = (label) => (0, import_yup2.number)().integer().min(0).max(1200).optional().default(void 0).label(label);
|
|
297
|
+
var percent = (label) => (0, import_yup2.number)().integer().min(0).max(100).optional().default(void 0).label(label);
|
|
298
|
+
var epoch = (label) => (0, import_yup2.number)().integer().min(0).optional().default(void 0).label(label).meta({ widget: "date" });
|
|
299
|
+
var currencyBound = (label, currencyField) => (0, import_yup2.number)().integer().min(0).optional().default(void 0).label(label).when(currencyField, {
|
|
300
|
+
is: (v) => !Array.isArray(v) || v.length !== 1,
|
|
301
|
+
then: (s) => s.test(
|
|
302
|
+
"requires-single-currency",
|
|
303
|
+
"Pick exactly one salary currency to filter by salary",
|
|
304
|
+
(value) => value === void 0 || value === null
|
|
305
|
+
)
|
|
306
|
+
});
|
|
307
|
+
var qualified = (qualifierValues, qualifierKey, label) => (0, import_yup2.array)().of(
|
|
308
|
+
// `rejectUnknownKeys`, not `.noUnknown()`, for the reason spelled out on
|
|
309
|
+
// that helper: outside strict mode `.noUnknown()` silently STRIPS an
|
|
310
|
+
// unknown key rather than failing, so a misspelled qualifier would arrive
|
|
311
|
+
// as a name-only entry and quietly widen the match instead of erroring.
|
|
312
|
+
rejectUnknownKeys(
|
|
313
|
+
(0, import_yup2.object)({
|
|
314
|
+
name: (0, import_yup2.string)().trim().required().label("Name"),
|
|
315
|
+
// `"open"` for a free-text qualifier (a certification's issuing body).
|
|
316
|
+
// An empty array would NOT mean "anything goes" — `.oneOf([])` rejects
|
|
317
|
+
// every value — so the open case is named rather than inferred from an
|
|
318
|
+
// empty vocabulary.
|
|
319
|
+
[qualifierKey]: qualifierValues === "open" ? (0, import_yup2.string)().trim().optional().default(void 0).label(label) : (0, import_yup2.string)().oneOf(qualifierValues).optional().default(void 0).label(label)
|
|
320
|
+
}).label(label).required(),
|
|
321
|
+
() => ["name", qualifierKey]
|
|
322
|
+
)
|
|
323
|
+
).optional().default([]).label(label);
|
|
324
|
+
var conjunctionFieldName = (f) => typeof f === "string" ? f : f.name;
|
|
325
|
+
var conjunction = (fields, label) => {
|
|
326
|
+
const names = fields.map(conjunctionFieldName);
|
|
327
|
+
const isBoolean = (name) => fields.some(
|
|
328
|
+
(f) => typeof f !== "string" && f.name === name && f.type === "boolean"
|
|
329
|
+
);
|
|
330
|
+
return (0, import_yup2.array)().of(
|
|
331
|
+
rejectUnknownKeys(
|
|
332
|
+
(0, import_yup2.object)(
|
|
333
|
+
Object.fromEntries(
|
|
334
|
+
fields.map((f) => {
|
|
335
|
+
const name = conjunctionFieldName(f);
|
|
336
|
+
if (typeof f !== "string" && f.type === "boolean") {
|
|
337
|
+
return [name, (0, import_yup2.boolean)().optional().default(void 0).label(name)];
|
|
338
|
+
}
|
|
339
|
+
const base = (0, import_yup2.string)().trim().optional().default(void 0).label(name);
|
|
340
|
+
return [
|
|
341
|
+
name,
|
|
342
|
+
typeof f !== "string" && f.oneOf ? base.oneOf([...f.oneOf, void 0]) : base
|
|
343
|
+
];
|
|
344
|
+
})
|
|
345
|
+
)
|
|
346
|
+
).test(
|
|
347
|
+
"at-least-one-field",
|
|
348
|
+
// Names the unknown key when that is the real problem. A misspelled
|
|
349
|
+
// field (`companyName`) is stripped before this test sees it, so it
|
|
350
|
+
// would otherwise report "needs at least one field set" — true, but
|
|
351
|
+
// it sends the reader looking in the wrong place.
|
|
352
|
+
({ value }) => {
|
|
353
|
+
const unknown = Object.keys(
|
|
354
|
+
value ?? {}
|
|
355
|
+
).filter((k) => !names.includes(k));
|
|
356
|
+
return unknown.length ? `${label} has unsupported keys: ${unknown.join(", ")}` : `${label} needs at least one of: ${names.join(", ")}`;
|
|
357
|
+
},
|
|
358
|
+
(value) => value == null || names.some((f) => {
|
|
359
|
+
const v = value[f];
|
|
360
|
+
if (isBoolean(f)) return typeof v === "boolean";
|
|
361
|
+
return typeof v === "string" && v.trim() !== "";
|
|
362
|
+
})
|
|
363
|
+
).label(label).required(),
|
|
364
|
+
() => names
|
|
365
|
+
)
|
|
366
|
+
).optional().default([]).label(label);
|
|
367
|
+
};
|
|
368
|
+
var rejectUnknownKeys = (schema, allowed) => schema.test(
|
|
369
|
+
"no-unknown-keys",
|
|
370
|
+
({ value, label }) => {
|
|
371
|
+
const known = new Set(allowed());
|
|
372
|
+
const bad = Object.keys(value ?? {}).filter(
|
|
373
|
+
(k) => !known.has(k)
|
|
374
|
+
);
|
|
375
|
+
return `${label} has unsupported keys: ${bad.join(", ")}`;
|
|
376
|
+
},
|
|
377
|
+
(value) => {
|
|
378
|
+
if (value == null) return true;
|
|
379
|
+
const known = new Set(allowed());
|
|
380
|
+
return Object.keys(value).every(
|
|
381
|
+
(k) => known.has(k)
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
);
|
|
385
|
+
|
|
217
386
|
// src/common/common.constant.ts
|
|
218
387
|
var Common = /* @__PURE__ */ ((Common2) => {
|
|
219
388
|
Common2["Yes"] = "yes";
|
|
@@ -270,12 +439,12 @@ var ExperienceLevel = /* @__PURE__ */ ((ExperienceLevel2) => {
|
|
|
270
439
|
var SupportedExperienceLevels = Object.values(ExperienceLevel);
|
|
271
440
|
|
|
272
441
|
// src/duration/duration.schema.ts
|
|
273
|
-
var
|
|
442
|
+
var import_yup4 = require("yup");
|
|
274
443
|
|
|
275
444
|
// src/extensions/date-string.extension.ts
|
|
276
445
|
var import_dayjs = __toESM(require("dayjs"), 1);
|
|
277
|
-
var
|
|
278
|
-
var DateStringSchema = class extends
|
|
446
|
+
var import_yup3 = require("yup");
|
|
447
|
+
var DateStringSchema = class extends import_yup3.MixedSchema {
|
|
279
448
|
constructor() {
|
|
280
449
|
super({
|
|
281
450
|
type: "mixed",
|
|
@@ -325,11 +494,11 @@ var DurationSchema = (options) => {
|
|
|
325
494
|
const { startKey = "startDate", endKey = "endDate", format } = options || {};
|
|
326
495
|
const startLabel = options?.startLabel || startKey;
|
|
327
496
|
const endLabel = options?.endLabel || endKey;
|
|
328
|
-
return (0,
|
|
497
|
+
return (0, import_yup4.object)().shape({
|
|
329
498
|
[startKey]: dateString().format(format, `${startLabel} must be in the format ${format}`).required(`${startLabel} is required`).typeError(`${startLabel} must be a valid date`),
|
|
330
499
|
[endKey]: dateString().format(format, `${endLabel} must be in the format ${format}`).when("isActive", {
|
|
331
500
|
is: (val) => Boolean(val) === false,
|
|
332
|
-
then: (schema) => schema.typeError(`${endLabel} must be a valid date`).min((0,
|
|
501
|
+
then: (schema) => schema.typeError(`${endLabel} must be a valid date`).min((0, import_yup4.ref)(startKey), `${endLabel} must be after ${startKey}`).required(`${endLabel} is required`),
|
|
333
502
|
otherwise: (schema) => schema.nullable().default(null).label(endLabel)
|
|
334
503
|
// .test(
|
|
335
504
|
// "notAllowed",
|
|
@@ -337,12 +506,12 @@ var DurationSchema = (options) => {
|
|
|
337
506
|
// (value) => !value
|
|
338
507
|
// ),ccc
|
|
339
508
|
}),
|
|
340
|
-
isActive: (0,
|
|
509
|
+
isActive: (0, import_yup4.boolean)().optional().default(false).typeError("isActive must be a boolean value")
|
|
341
510
|
});
|
|
342
511
|
};
|
|
343
512
|
|
|
344
513
|
// src/group/group.schema.ts
|
|
345
|
-
var
|
|
514
|
+
var import_yup5 = require("yup");
|
|
346
515
|
|
|
347
516
|
// src/group/group.constant.ts
|
|
348
517
|
var GroupVisibility = /* @__PURE__ */ ((GroupVisibility2) => {
|
|
@@ -366,80 +535,129 @@ var GroupStatus = /* @__PURE__ */ ((GroupStatus2) => {
|
|
|
366
535
|
GroupStatus2["Archived"] = "archived";
|
|
367
536
|
return GroupStatus2;
|
|
368
537
|
})(GroupStatus || {});
|
|
538
|
+
var SupportedGroupStatuses = Object.values(GroupStatus);
|
|
539
|
+
var SupportedGroupVisibilities = Object.values(GroupVisibility);
|
|
540
|
+
var SupportedGroupManagedBy = Object.values(GroupManagedBy);
|
|
541
|
+
var SupportedGroupMembershipStatuses = Object.values(
|
|
542
|
+
GroupMembershipStatus
|
|
543
|
+
);
|
|
369
544
|
|
|
370
545
|
// src/group/group.schema.ts
|
|
371
|
-
var GroupTranslationSchema = (0,
|
|
372
|
-
language: (0,
|
|
373
|
-
name: (0,
|
|
374
|
-
description: (0,
|
|
375
|
-
slug: (0,
|
|
376
|
-
seo: (0,
|
|
377
|
-
description: (0,
|
|
378
|
-
keywords: (0,
|
|
546
|
+
var GroupTranslationSchema = (0, import_yup5.object)({
|
|
547
|
+
language: (0, import_yup5.string)().required("Language is required"),
|
|
548
|
+
name: (0, import_yup5.string)().required("Name is required"),
|
|
549
|
+
description: (0, import_yup5.string)().optional(),
|
|
550
|
+
slug: (0, import_yup5.string)().optional(),
|
|
551
|
+
seo: (0, import_yup5.object)({
|
|
552
|
+
description: (0, import_yup5.string)().optional(),
|
|
553
|
+
keywords: (0, import_yup5.array)().of((0, import_yup5.string)().required()).optional()
|
|
379
554
|
}).optional()
|
|
380
555
|
});
|
|
381
|
-
var GroupSchema = (0,
|
|
382
|
-
name: (0,
|
|
383
|
-
description: (0,
|
|
384
|
-
slug: (0,
|
|
385
|
-
banner: (0,
|
|
386
|
-
logo: (0,
|
|
387
|
-
light: (0,
|
|
388
|
-
dark: (0,
|
|
556
|
+
var GroupSchema = (0, import_yup5.object)().shape({
|
|
557
|
+
name: (0, import_yup5.string)().required().min(3).max(100).label("Name"),
|
|
558
|
+
description: (0, import_yup5.string)().required().min(10).max(1e3).label("Description"),
|
|
559
|
+
slug: (0, import_yup5.string)().required().label("Slug"),
|
|
560
|
+
banner: (0, import_yup5.string)().label("Banner"),
|
|
561
|
+
logo: (0, import_yup5.object)().shape({
|
|
562
|
+
light: (0, import_yup5.string)().required().label("Light Logo"),
|
|
563
|
+
dark: (0, import_yup5.string)().optional().nullable().label("Dark Logo")
|
|
389
564
|
}).optional().nullable().default(null).label("Logo"),
|
|
390
|
-
isFeatured: (0,
|
|
391
|
-
isDiscoverable: (0,
|
|
392
|
-
featuredMarkets: (0,
|
|
393
|
-
followersCount: (0,
|
|
394
|
-
membersCount: (0,
|
|
395
|
-
visibility: (0,
|
|
396
|
-
groupRules: (0,
|
|
397
|
-
countries: (0,
|
|
398
|
-
filterQuery: (0,
|
|
399
|
-
jobs: (0,
|
|
400
|
-
courses: (0,
|
|
401
|
-
discussions: (0,
|
|
565
|
+
isFeatured: (0, import_yup5.boolean)().label("Is Featured"),
|
|
566
|
+
isDiscoverable: (0, import_yup5.boolean)().default(true).label("Is Discoverable"),
|
|
567
|
+
featuredMarkets: (0, import_yup5.array)().of((0, import_yup5.string)()).optional().default([]).label("Featured Markets"),
|
|
568
|
+
followersCount: (0, import_yup5.number)().min(0).label("Followers Count"),
|
|
569
|
+
membersCount: (0, import_yup5.number)().min(0).label("Members Count"),
|
|
570
|
+
visibility: (0, import_yup5.mixed)().oneOf(Object.values(GroupVisibility)).default("public" /* Public */).label("Visibility"),
|
|
571
|
+
groupRules: (0, import_yup5.string)().optional().label("Group Rules"),
|
|
572
|
+
countries: (0, import_yup5.array)().of((0, import_yup5.string)()).optional().label("Countries"),
|
|
573
|
+
filterQuery: (0, import_yup5.object)().shape({
|
|
574
|
+
jobs: (0, import_yup5.string)().label("Job"),
|
|
575
|
+
courses: (0, import_yup5.string)().label("Course"),
|
|
576
|
+
discussions: (0, import_yup5.string)().label("Discussion")
|
|
402
577
|
}).label("Filter Query"),
|
|
403
|
-
seo: (0,
|
|
404
|
-
description: (0,
|
|
405
|
-
keywords: (0,
|
|
578
|
+
seo: (0, import_yup5.object)().shape({
|
|
579
|
+
description: (0, import_yup5.string)().optional().label("Description"),
|
|
580
|
+
keywords: (0, import_yup5.array)().of((0, import_yup5.string)()).optional().label("Keywords")
|
|
406
581
|
}).optional().label("SEO"),
|
|
407
|
-
translation: (0,
|
|
408
|
-
managedBy: (0,
|
|
409
|
-
status: (0,
|
|
410
|
-
createdAt: (0,
|
|
411
|
-
updatedAt: (0,
|
|
582
|
+
translation: (0, import_yup5.array)().of(GroupTranslationSchema).optional().label("Translations"),
|
|
583
|
+
managedBy: (0, import_yup5.mixed)().oneOf(Object.values(GroupManagedBy)).default("community" /* Community */).label("Managed By"),
|
|
584
|
+
status: (0, import_yup5.mixed)().oneOf(Object.values(GroupStatus)).default("active" /* Active */).label("Status"),
|
|
585
|
+
createdAt: (0, import_yup5.number)().optional().label("Created At"),
|
|
586
|
+
updatedAt: (0, import_yup5.number)().optional().label("Updated At")
|
|
412
587
|
}).label("Group Schema");
|
|
413
|
-
var GroupMembershipSchema = (0,
|
|
414
|
-
groupId: (0,
|
|
415
|
-
userId: (0,
|
|
416
|
-
status: (0,
|
|
417
|
-
createdAt: (0,
|
|
418
|
-
updatedAt: (0,
|
|
588
|
+
var GroupMembershipSchema = (0, import_yup5.object)({
|
|
589
|
+
groupId: (0, import_yup5.string)().required().label("Group ID"),
|
|
590
|
+
userId: (0, import_yup5.string)().required().label("User ID"),
|
|
591
|
+
status: (0, import_yup5.mixed)().oneOf(Object.values(GroupMembershipStatus)).required().label("Status"),
|
|
592
|
+
createdAt: (0, import_yup5.number)().optional().label("Created At"),
|
|
593
|
+
updatedAt: (0, import_yup5.number)().optional().label("Updated At")
|
|
419
594
|
});
|
|
420
595
|
|
|
596
|
+
// src/group/group-filter.schema.ts
|
|
597
|
+
var import_yup6 = require("yup");
|
|
598
|
+
var GroupFilterShape = (0, import_yup6.object)({
|
|
599
|
+
// ── Classification ──────────────────────────────────────────────────────────
|
|
600
|
+
// Closed vocabularies, matched exactly. Values are LOWERCASE (`active`,
|
|
601
|
+
// `public`, `system`) and that casing is data, not style: it is what is stored.
|
|
602
|
+
//
|
|
603
|
+
// All three are currently NON-SELECTIVE — every group in production is
|
|
604
|
+
// `active` / `public` / `system`. They are declared anyway because they are
|
|
605
|
+
// real vocabulary that gains meaning as the corpus grows, and a filter that
|
|
606
|
+
// silently ignores `status` would be worse than one that narrows to everything.
|
|
607
|
+
status: enumFacet(SupportedGroupStatuses, "Status"),
|
|
608
|
+
visibility: enumFacet(SupportedGroupVisibilities, "Visibility"),
|
|
609
|
+
managedBy: enumFacet(SupportedGroupManagedBy, "Managed By"),
|
|
610
|
+
// ── Flags ───────────────────────────────────────────────────────────────────
|
|
611
|
+
// No `.default(false)`: an unchecked box must mean "no constraint", not "only
|
|
612
|
+
// the non-featured ones", so absent simply omits the clause.
|
|
613
|
+
isFeatured: (0, import_yup6.boolean)().optional().label("Featured Only"),
|
|
614
|
+
isDiscoverable: (0, import_yup6.boolean)().optional().label("Discoverable Only"),
|
|
615
|
+
// ── Markets ─────────────────────────────────────────────────────────────────
|
|
616
|
+
/**
|
|
617
|
+
* `tokens`, NOT `codeTokens`. This is the trap in this file.
|
|
618
|
+
*
|
|
619
|
+
* Every other filter here uses `codeTokens` for country codes, because users
|
|
620
|
+
* and jobs store uppercase ISO alpha-2. Groups store them LOWERCASE (`in`,
|
|
621
|
+
* `se`, `ww` — verified against production). `codeTokens` uppercases its
|
|
622
|
+
* input, so `$in: ["IN"]` against a stored `"in"` matches nothing: no error,
|
|
623
|
+
* empty result, empty mail.
|
|
624
|
+
*
|
|
625
|
+
* If these are ever normalised to uppercase, this becomes `codeTokens` in the
|
|
626
|
+
* same change as the data migration, not before it.
|
|
627
|
+
*/
|
|
628
|
+
countries: tokens("Countries"),
|
|
629
|
+
featuredMarkets: tokens("Featured Markets"),
|
|
630
|
+
/** Group SLUGS, for naming specific groups. A display name breaks on rename. */
|
|
631
|
+
slugs: tokens("Slugs")
|
|
632
|
+
}).label("Group Filter");
|
|
633
|
+
var GROUP_FILTER_KEYS = Object.keys(GroupFilterShape.fields);
|
|
634
|
+
var GroupFilterSchema = rejectUnknownKeys(
|
|
635
|
+
GroupFilterShape,
|
|
636
|
+
() => GROUP_FILTER_KEYS
|
|
637
|
+
);
|
|
638
|
+
|
|
421
639
|
// src/job/job.schema.ts
|
|
422
|
-
var
|
|
640
|
+
var import_yup20 = require("yup");
|
|
423
641
|
|
|
424
642
|
// src/location/location.schema.ts
|
|
425
|
-
var
|
|
426
|
-
var LocationSchema = (0,
|
|
427
|
-
placeId: (0,
|
|
428
|
-
country: (0,
|
|
429
|
-
countryCode: (0,
|
|
430
|
-
state: (0,
|
|
431
|
-
stateCode: (0,
|
|
432
|
-
city: (0,
|
|
433
|
-
address: (0,
|
|
434
|
-
geo: (0,
|
|
435
|
-
type: (0,
|
|
436
|
-
coordinates: (0,
|
|
643
|
+
var import_yup7 = require("yup");
|
|
644
|
+
var LocationSchema = (0, import_yup7.object)({
|
|
645
|
+
placeId: (0, import_yup7.string)().optional().nullable(),
|
|
646
|
+
country: (0, import_yup7.string)().required(),
|
|
647
|
+
countryCode: (0, import_yup7.string)().required(),
|
|
648
|
+
state: (0, import_yup7.string)().optional().nullable(),
|
|
649
|
+
stateCode: (0, import_yup7.string)().optional().nullable(),
|
|
650
|
+
city: (0, import_yup7.string)().optional().nullable(),
|
|
651
|
+
address: (0, import_yup7.string)().optional().nullable(),
|
|
652
|
+
geo: (0, import_yup7.object)({
|
|
653
|
+
type: (0, import_yup7.string)().oneOf(["Point"]).default("Point"),
|
|
654
|
+
coordinates: (0, import_yup7.array)().of((0, import_yup7.number)().required()).length(2).required()
|
|
437
655
|
// [longitude, latitude]
|
|
438
656
|
}).required()
|
|
439
657
|
});
|
|
440
658
|
|
|
441
659
|
// src/page/page.schema.ts
|
|
442
|
-
var
|
|
660
|
+
var import_yup14 = require("yup");
|
|
443
661
|
|
|
444
662
|
// src/page/page.constant.ts
|
|
445
663
|
var PageType = /* @__PURE__ */ ((PageType2) => {
|
|
@@ -477,44 +695,44 @@ var ContactTypes = /* @__PURE__ */ ((ContactTypes2) => {
|
|
|
477
695
|
var SupportedContactTypes = Object.values(ContactTypes);
|
|
478
696
|
|
|
479
697
|
// src/page/page-location.schema.ts
|
|
480
|
-
var
|
|
481
|
-
var PageLocationSchema = (0,
|
|
482
|
-
isHeadquarters: (0,
|
|
483
|
-
coverImage: (0,
|
|
484
|
-
name: (0,
|
|
698
|
+
var import_yup8 = require("yup");
|
|
699
|
+
var PageLocationSchema = (0, import_yup8.object)().shape({
|
|
700
|
+
isHeadquarters: (0, import_yup8.boolean)().oneOf([true, false]).default(false).label("Is Headquarters"),
|
|
701
|
+
coverImage: (0, import_yup8.string)().optional().label("Cover Image"),
|
|
702
|
+
name: (0, import_yup8.string)().required().label("Office Name"),
|
|
485
703
|
/* TODO: Add Location Schema to enable geo spacial search*/
|
|
486
704
|
// location: LocationSchema,
|
|
487
|
-
contact: (0,
|
|
488
|
-
email: (0,
|
|
489
|
-
phone: (0,
|
|
490
|
-
address: (0,
|
|
705
|
+
contact: (0, import_yup8.object)().shape({
|
|
706
|
+
email: (0, import_yup8.string)().email().optional().label("Email"),
|
|
707
|
+
phone: (0, import_yup8.string)().optional().label("Phone"),
|
|
708
|
+
address: (0, import_yup8.string)().required().label("Address")
|
|
491
709
|
})
|
|
492
710
|
}).label("PageLocationSchema");
|
|
493
711
|
|
|
494
712
|
// src/page/institute/institute.schema.ts
|
|
495
|
-
var
|
|
496
|
-
var InstituteSchema = (0,
|
|
713
|
+
var import_yup9 = require("yup");
|
|
714
|
+
var InstituteSchema = (0, import_yup9.object)().shape({}).label("InstituteSchema");
|
|
497
715
|
|
|
498
716
|
// src/page/company/company.schema.ts
|
|
499
|
-
var
|
|
717
|
+
var import_yup12 = require("yup");
|
|
500
718
|
|
|
501
719
|
// src/page/company/perks-and-benefits.schema.ts
|
|
502
|
-
var
|
|
503
|
-
var
|
|
504
|
-
var PerksAndBenefitSchema = (0,
|
|
505
|
-
name: (0,
|
|
506
|
-
description: (0,
|
|
507
|
-
category: (0,
|
|
720
|
+
var import_yup10 = require("yup");
|
|
721
|
+
var import_yup11 = require("yup");
|
|
722
|
+
var PerksAndBenefitSchema = (0, import_yup11.object)().shape({
|
|
723
|
+
name: (0, import_yup11.string)().trim().max(50).required().label("Name"),
|
|
724
|
+
description: (0, import_yup11.string)().max(1e3).required().label("Description"),
|
|
725
|
+
category: (0, import_yup10.array)().of((0, import_yup11.string)()).optional().label("Category")
|
|
508
726
|
});
|
|
509
727
|
|
|
510
728
|
// src/page/company/company.schema.ts
|
|
511
|
-
var CompanySchema = (0,
|
|
512
|
-
equalOpportunityEmployer: (0,
|
|
513
|
-
perksAndBenefits: (0,
|
|
729
|
+
var CompanySchema = (0, import_yup12.object)().shape({
|
|
730
|
+
equalOpportunityEmployer: (0, import_yup12.boolean)().optional().label("Equal Opportunity Employer"),
|
|
731
|
+
perksAndBenefits: (0, import_yup12.array)().of(PerksAndBenefitSchema).optional().label("Perks and Benefits")
|
|
514
732
|
}).noUnknown().strict().label("Company Schema");
|
|
515
733
|
|
|
516
734
|
// src/social-account/social-account.schema.ts
|
|
517
|
-
var
|
|
735
|
+
var import_yup13 = require("yup");
|
|
518
736
|
|
|
519
737
|
// src/social-account/social-account.constant.ts
|
|
520
738
|
var SocialAccount = /* @__PURE__ */ ((SocialAccount2) => {
|
|
@@ -552,43 +770,43 @@ var SocialAccount = /* @__PURE__ */ ((SocialAccount2) => {
|
|
|
552
770
|
var SupportedSocialAccounts = Object.values(SocialAccount);
|
|
553
771
|
|
|
554
772
|
// src/social-account/social-account.schema.ts
|
|
555
|
-
var SocialAccountSchema = (0,
|
|
773
|
+
var SocialAccountSchema = (0, import_yup13.object)().shape({
|
|
556
774
|
// TODO: Remove isNew - it's unused. Backend (user-social-accounts.service.ts) just strips it before saving.
|
|
557
|
-
isNew: (0,
|
|
558
|
-
type: (0,
|
|
559
|
-
url: (0,
|
|
775
|
+
isNew: (0, import_yup13.boolean)().default(false).optional().label("Is New"),
|
|
776
|
+
type: (0, import_yup13.string)().oneOf(SupportedSocialAccounts).required().label("Account Type"),
|
|
777
|
+
url: (0, import_yup13.string)().required().label("URL")
|
|
560
778
|
}).noUnknown().strict().label("Social Account");
|
|
561
779
|
|
|
562
780
|
// src/page/page.schema.ts
|
|
563
|
-
var PageSchema = (0,
|
|
564
|
-
id: (0,
|
|
565
|
-
shortId: (0,
|
|
566
|
-
name: (0,
|
|
567
|
-
headline: (0,
|
|
568
|
-
about: (0,
|
|
569
|
-
website: (0,
|
|
570
|
-
slug: (0,
|
|
571
|
-
employeeCount: (0,
|
|
572
|
-
yearFounded: (0,
|
|
573
|
-
type: (0,
|
|
574
|
-
locations: (0,
|
|
781
|
+
var PageSchema = (0, import_yup14.object)().shape({
|
|
782
|
+
id: (0, import_yup14.string)().required().label("ID"),
|
|
783
|
+
shortId: (0, import_yup14.string)().required().label("Short ID"),
|
|
784
|
+
name: (0, import_yup14.string)().required().label("Name"),
|
|
785
|
+
headline: (0, import_yup14.string)().max(100).optional().label("Headline"),
|
|
786
|
+
about: (0, import_yup14.string)().required().min(100).max(1e4).label("About"),
|
|
787
|
+
website: (0, import_yup14.string)().required().label("Website"),
|
|
788
|
+
slug: (0, import_yup14.string)().required().label("Slug"),
|
|
789
|
+
employeeCount: (0, import_yup14.string)().optional().label("Employee Count"),
|
|
790
|
+
yearFounded: (0, import_yup14.string)().optional().label("Year Founded"),
|
|
791
|
+
type: (0, import_yup14.string)().oneOf(SupportedPageTypes).required().label("Page Type"),
|
|
792
|
+
locations: (0, import_yup14.array)().of(PageLocationSchema).optional().label("Locations"),
|
|
575
793
|
/*
|
|
576
794
|
* @deprecated
|
|
577
795
|
* 'locations' prop already has contact information, so this can be removed
|
|
578
796
|
*/
|
|
579
|
-
contacts: (0,
|
|
580
|
-
(0,
|
|
581
|
-
type: (0,
|
|
582
|
-
value: (0,
|
|
583
|
-
label: (0,
|
|
584
|
-
isPrimary: (0,
|
|
585
|
-
isVerified: (0,
|
|
797
|
+
contacts: (0, import_yup14.array)().of(
|
|
798
|
+
(0, import_yup14.object)().shape({
|
|
799
|
+
type: (0, import_yup14.string)().oneOf(SupportedContactTypes).required().label("Type"),
|
|
800
|
+
value: (0, import_yup14.string)().required().label("Value"),
|
|
801
|
+
label: (0, import_yup14.string)().optional().label("Label"),
|
|
802
|
+
isPrimary: (0, import_yup14.boolean)().optional().default(false).label("Is Primary"),
|
|
803
|
+
isVerified: (0, import_yup14.boolean)().optional().default(false).label("Is Verified")
|
|
586
804
|
})
|
|
587
805
|
).optional().label("Contacts"),
|
|
588
|
-
verified: (0,
|
|
589
|
-
logo: (0,
|
|
590
|
-
dark: (0,
|
|
591
|
-
light: (0,
|
|
806
|
+
verified: (0, import_yup14.date)().optional().label("Verified"),
|
|
807
|
+
logo: (0, import_yup14.object)().shape({
|
|
808
|
+
dark: (0, import_yup14.string)().optional().nullable().label("Dark Logo"),
|
|
809
|
+
light: (0, import_yup14.string)().required().label("Light Logo")
|
|
592
810
|
}).optional().nullable().default(null).label("Logo"),
|
|
593
811
|
// Taxonomy (see job-taxonomy.constant.ts): category -> industry -> subCategory,
|
|
594
812
|
// stored as slugs and translated for display. As on JobSchema, these are NOT
|
|
@@ -596,21 +814,21 @@ var PageSchema = (0, import_yup12.object)().shape({
|
|
|
596
814
|
// never a reason to reject the whole page. `categories` is the top level (kept
|
|
597
815
|
// required for backward compatibility); `industries` and `subCategories` are the
|
|
598
816
|
// deeper levels the tree picker fills in when a leaf auto-selects its parents.
|
|
599
|
-
categories: (0,
|
|
600
|
-
industries: (0,
|
|
601
|
-
subCategories: (0,
|
|
602
|
-
socialAccounts: (0,
|
|
603
|
-
isOwner: (0,
|
|
604
|
-
isFeatured: (0,
|
|
605
|
-
featuredMarkets: (0,
|
|
606
|
-
status: (0,
|
|
607
|
-
followersCount: (0,
|
|
817
|
+
categories: (0, import_yup14.array)().of((0, import_yup14.string)().trim().required()).required().min(1).label("Categories"),
|
|
818
|
+
industries: (0, import_yup14.array)().of((0, import_yup14.string)().trim().required()).optional().default([]).label("Industries"),
|
|
819
|
+
subCategories: (0, import_yup14.array)().of((0, import_yup14.string)().trim().required()).optional().default([]).label("Sub Categories"),
|
|
820
|
+
socialAccounts: (0, import_yup14.array)().of(SocialAccountSchema).optional().label("Social Accounts"),
|
|
821
|
+
isOwner: (0, import_yup14.boolean)().optional().label("Is Owner"),
|
|
822
|
+
isFeatured: (0, import_yup14.boolean)().optional().default(false).label("Is Featured"),
|
|
823
|
+
featuredMarkets: (0, import_yup14.array)().of((0, import_yup14.string)().required()).optional().default([]).label("Featured Markets"),
|
|
824
|
+
status: (0, import_yup14.string)().oneOf(SupportedPageStatuses).required().label("Status"),
|
|
825
|
+
followersCount: (0, import_yup14.number)().min(0).optional().label("Followers Count")
|
|
608
826
|
}).concat(CompanySchema).concat(InstituteSchema).concat(DbDefaultSchema).noUnknown(true).strict(true).required().label("Page Schema");
|
|
609
827
|
|
|
610
828
|
// src/skill/skill.schema.ts
|
|
611
|
-
var
|
|
612
|
-
var SkillSchema = (0,
|
|
613
|
-
name: (0,
|
|
829
|
+
var import_yup15 = require("yup");
|
|
830
|
+
var SkillSchema = (0, import_yup15.object)({
|
|
831
|
+
name: (0, import_yup15.string)().trim().required().label("Skill Name"),
|
|
614
832
|
/**
|
|
615
833
|
* The taxonomy concept this skill resolved to, assigned by thejob-taxonomy-service
|
|
616
834
|
* via `POST /normalize/batch`. Stable slug, e.g. "power-bi".
|
|
@@ -628,14 +846,14 @@ var SkillSchema = (0, import_yup13.object)({
|
|
|
628
846
|
*
|
|
629
847
|
* Consumers must therefore read `conceptName ?? name`, never `conceptName` alone.
|
|
630
848
|
*/
|
|
631
|
-
conceptId: (0,
|
|
849
|
+
conceptId: (0, import_yup15.string)().optional().nullable().label("Concept ID"),
|
|
632
850
|
/** Canonical display name for `conceptId`, e.g. "Power BI". See `conceptId`. */
|
|
633
|
-
conceptName: (0,
|
|
634
|
-
logo: (0,
|
|
635
|
-
light: (0,
|
|
636
|
-
dark: (0,
|
|
851
|
+
conceptName: (0, import_yup15.string)().optional().nullable().label("Concept Name"),
|
|
852
|
+
logo: (0, import_yup15.object)({
|
|
853
|
+
light: (0, import_yup15.string)().required().label("Light Logo"),
|
|
854
|
+
dark: (0, import_yup15.string)().optional().nullable().label("Dark Logo")
|
|
637
855
|
}).optional().nullable().default(null).label("Logo"),
|
|
638
|
-
tags: (0,
|
|
856
|
+
tags: (0, import_yup15.array)().of((0, import_yup15.string)().max(50)).max(20).optional().label("Tags")
|
|
639
857
|
}).concat(DbDefaultSchema).noUnknown().strict().label("Skill");
|
|
640
858
|
|
|
641
859
|
// src/question/question.constant.ts
|
|
@@ -654,24 +872,24 @@ var AnswerChoiceType = /* @__PURE__ */ ((AnswerChoiceType2) => {
|
|
|
654
872
|
var SupportedAnswerChoiceTypes = Object.values(AnswerChoiceType);
|
|
655
873
|
|
|
656
874
|
// src/question/question.schema.ts
|
|
657
|
-
var
|
|
658
|
-
var QuestionSchema = (0,
|
|
659
|
-
label: (0,
|
|
660
|
-
default: (0,
|
|
661
|
-
title: (0,
|
|
662
|
-
type: (0,
|
|
663
|
-
isOptional: (0,
|
|
664
|
-
readonly: (0,
|
|
665
|
-
isNew: (0,
|
|
875
|
+
var import_yup16 = require("yup");
|
|
876
|
+
var QuestionSchema = (0, import_yup16.object)().shape({
|
|
877
|
+
label: (0, import_yup16.string)().max(50).optional().label("Label"),
|
|
878
|
+
default: (0, import_yup16.boolean)().optional().default(false).label("Default"),
|
|
879
|
+
title: (0, import_yup16.string)().required().max(200).label("Title"),
|
|
880
|
+
type: (0, import_yup16.string)().oneOf(SupportedQuestionTypes).required().label("Type"),
|
|
881
|
+
isOptional: (0, import_yup16.boolean)().optional().label("Mark if the question is optional"),
|
|
882
|
+
readonly: (0, import_yup16.boolean)().optional().label("Readonly"),
|
|
883
|
+
isNew: (0, import_yup16.boolean)().optional().label("Is New")
|
|
666
884
|
});
|
|
667
885
|
|
|
668
886
|
// src/question/input-question.schema.ts
|
|
669
|
-
var
|
|
887
|
+
var import_yup17 = require("yup");
|
|
670
888
|
var InputQuestionSchema = QuestionSchema.concat(
|
|
671
|
-
(0,
|
|
672
|
-
type: (0,
|
|
673
|
-
preferredAnswer: (0,
|
|
674
|
-
answers: (0,
|
|
889
|
+
(0, import_yup17.object)().shape({
|
|
890
|
+
type: (0, import_yup17.string)().oneOf(["input" /* Input */]).required().label("Question type"),
|
|
891
|
+
preferredAnswer: (0, import_yup17.string)().max(100).optional().label("Preferred Answer"),
|
|
892
|
+
answers: (0, import_yup17.string)().max(500).when("isOptional", {
|
|
675
893
|
is: (isOptional) => Boolean(isOptional),
|
|
676
894
|
then: (schema) => schema.optional(),
|
|
677
895
|
otherwise: (schema) => schema.required()
|
|
@@ -680,10 +898,10 @@ var InputQuestionSchema = QuestionSchema.concat(
|
|
|
680
898
|
);
|
|
681
899
|
|
|
682
900
|
// src/question/choice-question.schema.ts
|
|
683
|
-
var
|
|
684
|
-
var ChoicePreferredAnswerSchema = (label = "Preferred answer") => (0,
|
|
901
|
+
var import_yup18 = require("yup");
|
|
902
|
+
var ChoicePreferredAnswerSchema = (label = "Preferred answer") => (0, import_yup18.mixed)().when("answerChoiceType", {
|
|
685
903
|
is: (value) => value === "multiple" /* Multiple */,
|
|
686
|
-
then: () => (0,
|
|
904
|
+
then: () => (0, import_yup18.array)((0, import_yup18.string)()).test(
|
|
687
905
|
"is-valid-option",
|
|
688
906
|
`${label} must be from the provided options`,
|
|
689
907
|
function(answer) {
|
|
@@ -694,7 +912,7 @@ var ChoicePreferredAnswerSchema = (label = "Preferred answer") => (0, import_yup
|
|
|
694
912
|
return true;
|
|
695
913
|
}
|
|
696
914
|
).optional().label(label),
|
|
697
|
-
otherwise: () => (0,
|
|
915
|
+
otherwise: () => (0, import_yup18.string)().test(
|
|
698
916
|
"is-valid-option",
|
|
699
917
|
`${label} must be from the provided options`,
|
|
700
918
|
function(answer) {
|
|
@@ -705,20 +923,20 @@ var ChoicePreferredAnswerSchema = (label = "Preferred answer") => (0, import_yup
|
|
|
705
923
|
});
|
|
706
924
|
var getChoiceAnswerSchema = (answerChoiceType, isOptional) => {
|
|
707
925
|
if (answerChoiceType === "multiple" /* Multiple */) {
|
|
708
|
-
const multiAnswerSchema = (0,
|
|
926
|
+
const multiAnswerSchema = (0, import_yup18.array)((0, import_yup18.string)().max(500));
|
|
709
927
|
return isOptional ? multiAnswerSchema.optional().default([]) : multiAnswerSchema.min(1, "Select at least one option.").required("Select at least one option.").default([]);
|
|
710
928
|
}
|
|
711
|
-
const singleAnswerSchema = (0,
|
|
929
|
+
const singleAnswerSchema = (0, import_yup18.string)().max(500);
|
|
712
930
|
return isOptional ? singleAnswerSchema.optional() : singleAnswerSchema.required("Select an option.");
|
|
713
931
|
};
|
|
714
932
|
var ChoiceQuestionSchema = QuestionSchema.concat(
|
|
715
|
-
(0,
|
|
716
|
-
type: (0,
|
|
717
|
-
optionsFrom: (0,
|
|
718
|
-
options: (0,
|
|
719
|
-
answerChoiceType: (0,
|
|
933
|
+
(0, import_yup18.object)().shape({
|
|
934
|
+
type: (0, import_yup18.string)().oneOf(["choice" /* Choice */]).required().label("Question type"),
|
|
935
|
+
optionsFrom: (0, import_yup18.string)().oneOf(["skill" /* Skill */, "education_level" /* EducationLevel */, "experience_level" /* ExperienceLevel */]).optional().label("Option field"),
|
|
936
|
+
options: (0, import_yup18.array)((0, import_yup18.string)().required()).min(1).required().label("Options"),
|
|
937
|
+
answerChoiceType: (0, import_yup18.string)().oneOf(SupportedAnswerChoiceTypes).required().default("single" /* Single */).label("Type"),
|
|
720
938
|
preferredAnswer: ChoicePreferredAnswerSchema("Preferred answer"),
|
|
721
|
-
answers: (0,
|
|
939
|
+
answers: (0, import_yup18.mixed)().when(
|
|
722
940
|
["answerChoiceType", "isOptional"],
|
|
723
941
|
([answerChoiceType, isOptional]) => getChoiceAnswerSchema(answerChoiceType, isOptional)
|
|
724
942
|
)
|
|
@@ -726,12 +944,12 @@ var ChoiceQuestionSchema = QuestionSchema.concat(
|
|
|
726
944
|
);
|
|
727
945
|
|
|
728
946
|
// src/question/read-and-acknowledge.schema.ts
|
|
729
|
-
var
|
|
730
|
-
var AcknowledgeAnswerSchema = (0,
|
|
947
|
+
var import_yup19 = require("yup");
|
|
948
|
+
var AcknowledgeAnswerSchema = (0, import_yup19.string)().oneOf(["yes" /* Yes */, "no" /* No */]).required("This question must be acknowledged.");
|
|
731
949
|
var ReadAndAcknowledgeQuestionSchema = QuestionSchema.concat(
|
|
732
|
-
(0,
|
|
733
|
-
type: (0,
|
|
734
|
-
description: (0,
|
|
950
|
+
(0, import_yup19.object)().shape({
|
|
951
|
+
type: (0, import_yup19.string)().oneOf(["readAndAcknowledge" /* ReadAndAcknowledge */]).required().label("Question type"),
|
|
952
|
+
description: (0, import_yup19.string)().max(1e4).required().label("Content"),
|
|
735
953
|
preferredAnswer: AcknowledgeAnswerSchema.label("Preferred Answer"),
|
|
736
954
|
answers: AcknowledgeAnswerSchema.label("Answer")
|
|
737
955
|
})
|
|
@@ -812,20 +1030,20 @@ var SupportedApplicationReceivePreferences = Object.values(
|
|
|
812
1030
|
);
|
|
813
1031
|
|
|
814
1032
|
// src/job/job.schema.ts
|
|
815
|
-
var JobSchema = (0,
|
|
1033
|
+
var JobSchema = (0, import_yup20.object)({
|
|
816
1034
|
/** Unique id usable by external systems to reference the job. */
|
|
817
|
-
uniqueId: (0,
|
|
1035
|
+
uniqueId: (0, import_yup20.string)().optional().label("Unique ID"),
|
|
818
1036
|
/** Job title shown in listings and search. */
|
|
819
|
-
headline: (0,
|
|
1037
|
+
headline: (0, import_yup20.string)().required().min(5).max(150).label("Headline"),
|
|
820
1038
|
/** User-friendly, unique URL identifier. */
|
|
821
|
-
slug: (0,
|
|
1039
|
+
slug: (0, import_yup20.string)().required().max(250).label("Slug"),
|
|
822
1040
|
/** How applications are received (inbox vs external website). */
|
|
823
|
-
applicationReceivePreference: (0,
|
|
1041
|
+
applicationReceivePreference: (0, import_yup20.mixed)().oneOf([
|
|
824
1042
|
"inbox" /* Inbox */,
|
|
825
1043
|
"external_website" /* ExternalWebsite */
|
|
826
1044
|
]).required().label("Preference"),
|
|
827
1045
|
/** Required when applicationReceivePreference is ExternalWebsite. */
|
|
828
|
-
externalApplyLink: (0,
|
|
1046
|
+
externalApplyLink: (0, import_yup20.string)().when("applicationReceivePreference", {
|
|
829
1047
|
is: (value) => value === "external_website" /* ExternalWebsite */,
|
|
830
1048
|
then: (schema) => schema.url().required().label("Apply for Job link"),
|
|
831
1049
|
otherwise: (schema) => schema.optional()
|
|
@@ -833,35 +1051,35 @@ var JobSchema = (0, import_yup18.object)({
|
|
|
833
1051
|
/** Company the job belongs to (subset of PageSchema). */
|
|
834
1052
|
company: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Company"),
|
|
835
1053
|
/** @deprecated kept for backwards compatibility. */
|
|
836
|
-
designation: (0,
|
|
837
|
-
employmentType: (0,
|
|
838
|
-
workMode: (0,
|
|
1054
|
+
designation: (0, import_yup20.string)().optional().nullable().label("Designation"),
|
|
1055
|
+
employmentType: (0, import_yup20.string)().required().label("Employment type"),
|
|
1056
|
+
workMode: (0, import_yup20.string)().required().label("Work mode"),
|
|
839
1057
|
/**
|
|
840
1058
|
* `conceptId`/`conceptName` carry the taxonomy-service normalization. They are
|
|
841
1059
|
* picked here (not just `name`/`logo`) because SkillSchema is `.noUnknown()`:
|
|
842
1060
|
* an unpicked field that is actually PRESENT fails validation, so omitting them
|
|
843
1061
|
* would reject every normalized job.
|
|
844
1062
|
*/
|
|
845
|
-
skills: (0,
|
|
1063
|
+
skills: (0, import_yup20.array)().of(
|
|
846
1064
|
SkillSchema.pick(["name", "logo", "conceptId", "conceptName"]).required().label("Skill")
|
|
847
1065
|
).nullable().optional().label("Skills"),
|
|
848
|
-
bookmarks: (0,
|
|
849
|
-
embedding: (0,
|
|
850
|
-
vector: (0,
|
|
851
|
-
model: (0,
|
|
1066
|
+
bookmarks: (0, import_yup20.array)().of(UserIdAndCreatedAtSchema).optional().label("Bookmarks"),
|
|
1067
|
+
embedding: (0, import_yup20.object)({
|
|
1068
|
+
vector: (0, import_yup20.array)((0, import_yup20.number)().required()).optional().label("Embedding vector"),
|
|
1069
|
+
model: (0, import_yup20.string)().optional().label("Embedding model")
|
|
852
1070
|
}).nullable().optional().label("Embedding"),
|
|
853
|
-
applicants: (0,
|
|
854
|
-
applicantCount: (0,
|
|
855
|
-
externalApplicationLinkClickCount: (0,
|
|
856
|
-
description: (0,
|
|
857
|
-
descriptionMarkdown: (0,
|
|
858
|
-
descriptionHTML: (0,
|
|
1071
|
+
applicants: (0, import_yup20.array)().of(UserIdAndCreatedAtSchema).optional().label("Applicants"),
|
|
1072
|
+
applicantCount: (0, import_yup20.number)().optional().label("Applicant count"),
|
|
1073
|
+
externalApplicationLinkClickCount: (0, import_yup20.number)().optional().label("External application link click count"),
|
|
1074
|
+
description: (0, import_yup20.string)().required().min(100).max(5e4).label("Description"),
|
|
1075
|
+
descriptionMarkdown: (0, import_yup20.string)().required().label("Description Markdown"),
|
|
1076
|
+
descriptionHTML: (0, import_yup20.string)().optional().label("Description HTML"),
|
|
859
1077
|
/**
|
|
860
1078
|
* Job locations. Required even for remote roles (compliance/legal). Defaults
|
|
861
1079
|
* to an empty array; min(1) is intentionally not enforced yet.
|
|
862
1080
|
*/
|
|
863
|
-
locations: (0,
|
|
864
|
-
educationLevel: (0,
|
|
1081
|
+
locations: (0, import_yup20.array)().of(LocationSchema).default([]).label("Location"),
|
|
1082
|
+
educationLevel: (0, import_yup20.array)((0, import_yup20.string)().oneOf(SupportedEducationLevels)).required().min(1).label("Educational level"),
|
|
865
1083
|
/** Posting validity window (start/expiry). */
|
|
866
1084
|
validity: DurationSchema({
|
|
867
1085
|
format: SYSTEM_DATE_FORMAT,
|
|
@@ -874,22 +1092,22 @@ var JobSchema = (0, import_yup18.object)({
|
|
|
874
1092
|
* has no `validity.endDate` and must be verified by crawling its page. Guards
|
|
875
1093
|
* re-check cadence so the same job is not re-crawled on every sweep.
|
|
876
1094
|
*/
|
|
877
|
-
lastExpiryCheckAt: (0,
|
|
1095
|
+
lastExpiryCheckAt: (0, import_yup20.number)().optional().nullable().label("Last expiry check at"),
|
|
878
1096
|
/**
|
|
879
1097
|
* When this job transitioned to `status: closed` (epoch ms). Server-owned
|
|
880
1098
|
* operational field: stamped by every jobs-service close path (expiry
|
|
881
1099
|
* auto-close and the dated lapse sweep). Distinct from `updatedAt` so a later
|
|
882
1100
|
* edit to a closed job does not move it.
|
|
883
1101
|
*/
|
|
884
|
-
closedAt: (0,
|
|
885
|
-
experienceLevel: (0,
|
|
886
|
-
contactEmail: (0,
|
|
887
|
-
workingHoursPerWeek: (0,
|
|
888
|
-
tags: (0,
|
|
1102
|
+
closedAt: (0, import_yup20.number)().optional().nullable().label("Closed at"),
|
|
1103
|
+
experienceLevel: (0, import_yup20.string)().oneOf(SupportedExperienceLevels).nullable().optional().label("Experience level"),
|
|
1104
|
+
contactEmail: (0, import_yup20.string)().email().nullable().optional().label("Contact email"),
|
|
1105
|
+
workingHoursPerWeek: (0, import_yup20.number)().nullable().optional().min(1).max(24 * 7).label("Working hours per week"),
|
|
1106
|
+
tags: (0, import_yup20.array)().max(50).optional().label("Tags"),
|
|
889
1107
|
/** Direct/Quick-apply questionnaire. Each item is validated against the
|
|
890
1108
|
* concrete question schema for its `type`, with the stored `answers` omitted. */
|
|
891
|
-
questionnaire: (0,
|
|
892
|
-
(0,
|
|
1109
|
+
questionnaire: (0, import_yup20.array)().of(
|
|
1110
|
+
(0, import_yup20.lazy)((question) => {
|
|
893
1111
|
const schema = getSchemaByQuestion(question.type);
|
|
894
1112
|
if (question.type === "input" /* Input */) {
|
|
895
1113
|
return schema.omit([
|
|
@@ -905,12 +1123,12 @@ var JobSchema = (0, import_yup18.object)({
|
|
|
905
1123
|
return schema;
|
|
906
1124
|
})
|
|
907
1125
|
).label("Questionnaire"),
|
|
908
|
-
status: (0,
|
|
909
|
-
seoTags: (0,
|
|
910
|
-
description: (0,
|
|
911
|
-
keywords: (0,
|
|
1126
|
+
status: (0, import_yup20.string)().oneOf(SupportedJobStatuses).default("draft" /* Draft */).required().label("Status"),
|
|
1127
|
+
seoTags: (0, import_yup20.object)({
|
|
1128
|
+
description: (0, import_yup20.string)().optional().label("Description"),
|
|
1129
|
+
keywords: (0, import_yup20.array)().of((0, import_yup20.string)()).optional().label("Keywords")
|
|
912
1130
|
}).nullable().optional().label("SEO Tags"),
|
|
913
|
-
jdSummary: (0,
|
|
1131
|
+
jdSummary: (0, import_yup20.string)().optional().label("JD Summary"),
|
|
914
1132
|
/**
|
|
915
1133
|
* Structured, model-extracted sections of the posting, re-organized into clean
|
|
916
1134
|
* bullet points. This is our OWN unique presentation of the job (rendered as the
|
|
@@ -919,28 +1137,28 @@ var JobSchema = (0, import_yup18.object)({
|
|
|
919
1137
|
* optional: absent on jobs not yet (re-)enriched, and any section is `[]`/omitted
|
|
920
1138
|
* when the posting genuinely has none.
|
|
921
1139
|
*/
|
|
922
|
-
jobHighlights: (0,
|
|
923
|
-
responsibilities: (0,
|
|
924
|
-
requirements: (0,
|
|
925
|
-
benefits: (0,
|
|
926
|
-
qualifications: (0,
|
|
1140
|
+
jobHighlights: (0, import_yup20.object)({
|
|
1141
|
+
responsibilities: (0, import_yup20.array)().of((0, import_yup20.string)()).optional().label("Responsibilities"),
|
|
1142
|
+
requirements: (0, import_yup20.array)().of((0, import_yup20.string)()).optional().label("Requirements"),
|
|
1143
|
+
benefits: (0, import_yup20.array)().of((0, import_yup20.string)()).optional().label("Benefits"),
|
|
1144
|
+
qualifications: (0, import_yup20.array)().of((0, import_yup20.string)()).optional().label("Qualifications")
|
|
927
1145
|
}).nullable().optional().label("Job Highlights"),
|
|
928
|
-
canCreateAlert: (0,
|
|
929
|
-
canDirectApply: (0,
|
|
930
|
-
hasApplied: (0,
|
|
931
|
-
isOwner: (0,
|
|
932
|
-
hasBookmarked: (0,
|
|
933
|
-
hasReported: (0,
|
|
934
|
-
ratePerHour: (0,
|
|
935
|
-
isPromoted: (0,
|
|
936
|
-
salaryRange: (0,
|
|
937
|
-
currency: (0,
|
|
938
|
-
min: (0,
|
|
939
|
-
max: (0,
|
|
940
|
-
compensationType: (0,
|
|
941
|
-
isNegotiable: (0,
|
|
1146
|
+
canCreateAlert: (0, import_yup20.boolean)().optional().label("Can create alert"),
|
|
1147
|
+
canDirectApply: (0, import_yup20.boolean)().optional().label("Can direct apply"),
|
|
1148
|
+
hasApplied: (0, import_yup20.boolean)().optional().label("Has applied"),
|
|
1149
|
+
isOwner: (0, import_yup20.boolean)().optional().label("Is owner"),
|
|
1150
|
+
hasBookmarked: (0, import_yup20.boolean)().optional().label("Has bookmarked"),
|
|
1151
|
+
hasReported: (0, import_yup20.boolean)().optional().label("Has reported"),
|
|
1152
|
+
ratePerHour: (0, import_yup20.number)().optional().min(0).label("Rate per hour"),
|
|
1153
|
+
isPromoted: (0, import_yup20.boolean)().optional().label("Is promoted"),
|
|
1154
|
+
salaryRange: (0, import_yup20.object)({
|
|
1155
|
+
currency: (0, import_yup20.string)().required().label("Currency").default("USD"),
|
|
1156
|
+
min: (0, import_yup20.number)().nullable().optional().min(0).label("Minimum Salary"),
|
|
1157
|
+
max: (0, import_yup20.number)().nullable().optional().min(0).label("Maximum Salary"),
|
|
1158
|
+
compensationType: (0, import_yup20.string)().oneOf(SupportedCompensationTypes).nullable().optional().label("Compensation Type"),
|
|
1159
|
+
isNegotiable: (0, import_yup20.boolean)().nullable().optional().default(true).label("Is Negotiable")
|
|
942
1160
|
}).nullable().optional().label("Salary Range"),
|
|
943
|
-
reports: (0,
|
|
1161
|
+
reports: (0, import_yup20.array)().of(UserIdAndCreatedAtSchema).optional().label("Reports"),
|
|
944
1162
|
// Taxonomy fields are constants (see job-taxonomy.constant.ts), stored as slugs and
|
|
945
1163
|
// translated for display. They are deliberately NOT `oneOf`-constrained here: a value
|
|
946
1164
|
// outside the taxonomy must be DROPPED, never a reason to reject the posting. Yup
|
|
@@ -953,15 +1171,135 @@ var JobSchema = (0, import_yup18.object)({
|
|
|
953
1171
|
// time (values get renamed, retired, added), so stored jobs will always outlive the
|
|
954
1172
|
// enum — being lenient here is what lets the taxonomy evolve without a migration
|
|
955
1173
|
// gate on every write.
|
|
956
|
-
category: (0,
|
|
957
|
-
industry: (0,
|
|
958
|
-
subCategories: (0,
|
|
959
|
-
termsAccepted: (0,
|
|
960
|
-
isFeatured: (0,
|
|
1174
|
+
category: (0, import_yup20.string)().nullable().optional().label("Category"),
|
|
1175
|
+
industry: (0, import_yup20.string)().nullable().optional().label("Industry"),
|
|
1176
|
+
subCategories: (0, import_yup20.array)().of((0, import_yup20.string)().trim().required()).nullable().optional().label("Sub Categories"),
|
|
1177
|
+
termsAccepted: (0, import_yup20.boolean)().oneOf([true], "Accept terms before proceeding").required("Accept terms before proceeding").label("Terms accepted"),
|
|
1178
|
+
isFeatured: (0, import_yup20.boolean)().optional().default(false).label("Is featured"),
|
|
961
1179
|
/** Market country codes to feature this job in; empty = global. */
|
|
962
|
-
featuredMarkets: (0,
|
|
1180
|
+
featuredMarkets: (0, import_yup20.array)().of((0, import_yup20.string)().required()).optional().default([]).label("Featured markets")
|
|
963
1181
|
}).concat(DbDefaultSchema).noUnknown().label("Job");
|
|
964
1182
|
|
|
1183
|
+
// src/job/job-filter.schema.ts
|
|
1184
|
+
var import_yup21 = require("yup");
|
|
1185
|
+
|
|
1186
|
+
// src/user/user.constant.ts
|
|
1187
|
+
var UserRole = /* @__PURE__ */ ((UserRole2) => {
|
|
1188
|
+
UserRole2["Admin"] = "admin";
|
|
1189
|
+
UserRole2["Moderator"] = "moderator";
|
|
1190
|
+
UserRole2["Manager"] = "manager";
|
|
1191
|
+
UserRole2["Recruiter"] = "recruiter";
|
|
1192
|
+
UserRole2["JobSeeker"] = "job_seeker";
|
|
1193
|
+
UserRole2["Coordinator"] = "coordinator";
|
|
1194
|
+
UserRole2["System"] = "system";
|
|
1195
|
+
UserRole2["Normal"] = "normal";
|
|
1196
|
+
return UserRole2;
|
|
1197
|
+
})(UserRole || {});
|
|
1198
|
+
var SupportedUserRoles = Object.values(UserRole);
|
|
1199
|
+
var UserStatus = /* @__PURE__ */ ((UserStatus2) => {
|
|
1200
|
+
UserStatus2["Active"] = "active";
|
|
1201
|
+
UserStatus2["Blocked"] = "blocked";
|
|
1202
|
+
UserStatus2["Pending"] = "pending";
|
|
1203
|
+
UserStatus2["Deleted"] = "deleted";
|
|
1204
|
+
UserStatus2["MarkedForDeletion"] = "marked_for_deletion";
|
|
1205
|
+
return UserStatus2;
|
|
1206
|
+
})(UserStatus || {});
|
|
1207
|
+
var SupportedUserStatuses = Object.values(UserStatus);
|
|
1208
|
+
var DefaultUserRoles = ["normal" /* Normal */];
|
|
1209
|
+
var UserProfileVisibility = /* @__PURE__ */ ((UserProfileVisibility2) => {
|
|
1210
|
+
UserProfileVisibility2["Public"] = "public";
|
|
1211
|
+
UserProfileVisibility2["Private"] = "private";
|
|
1212
|
+
UserProfileVisibility2["Hidden"] = "hidden";
|
|
1213
|
+
return UserProfileVisibility2;
|
|
1214
|
+
})(UserProfileVisibility || {});
|
|
1215
|
+
var SupportedUserProfileVisibilities = Object.values(
|
|
1216
|
+
UserProfileVisibility
|
|
1217
|
+
);
|
|
1218
|
+
var SignupContextSource = /* @__PURE__ */ ((SignupContextSource2) => {
|
|
1219
|
+
SignupContextSource2["CfHeader"] = "cf-header";
|
|
1220
|
+
SignupContextSource2["Location"] = "location";
|
|
1221
|
+
SignupContextSource2["Default"] = "default";
|
|
1222
|
+
return SignupContextSource2;
|
|
1223
|
+
})(SignupContextSource || {});
|
|
1224
|
+
var SupportedSignupContextSources = Object.values(SignupContextSource);
|
|
1225
|
+
var UserDetailType = /* @__PURE__ */ ((UserDetailType2) => {
|
|
1226
|
+
UserDetailType2["Overview"] = "overview";
|
|
1227
|
+
UserDetailType2["AdditionalInfo"] = "additionalInfo";
|
|
1228
|
+
UserDetailType2["General"] = "general";
|
|
1229
|
+
UserDetailType2["SocialAccounts"] = "socialAccounts";
|
|
1230
|
+
UserDetailType2["WorkExperiences"] = "workExperiences";
|
|
1231
|
+
UserDetailType2["Educations"] = "educations";
|
|
1232
|
+
UserDetailType2["Skills"] = "skills";
|
|
1233
|
+
UserDetailType2["Languages"] = "languages";
|
|
1234
|
+
UserDetailType2["Certifications"] = "certifications";
|
|
1235
|
+
UserDetailType2["Interests"] = "interests";
|
|
1236
|
+
UserDetailType2["Projects"] = "projects";
|
|
1237
|
+
return UserDetailType2;
|
|
1238
|
+
})(UserDetailType || {});
|
|
1239
|
+
var ProficiencyLevel = /* @__PURE__ */ ((ProficiencyLevel2) => {
|
|
1240
|
+
ProficiencyLevel2["BasicAwareness"] = "basic_awareness";
|
|
1241
|
+
ProficiencyLevel2["Beginner"] = "beginner";
|
|
1242
|
+
ProficiencyLevel2["Intermediate"] = "intermediate";
|
|
1243
|
+
ProficiencyLevel2["Advanced"] = "advanced";
|
|
1244
|
+
ProficiencyLevel2["Expert"] = "expert";
|
|
1245
|
+
return ProficiencyLevel2;
|
|
1246
|
+
})(ProficiencyLevel || {});
|
|
1247
|
+
var SupportedProficiencyLevels = Object.values(ProficiencyLevel);
|
|
1248
|
+
var JobSearchUrgency = /* @__PURE__ */ ((JobSearchUrgency2) => {
|
|
1249
|
+
JobSearchUrgency2["Asap"] = "asap";
|
|
1250
|
+
JobSearchUrgency2["WithinThreeMonths"] = "within_3_months";
|
|
1251
|
+
JobSearchUrgency2["WithinSixMonths"] = "within_6_months";
|
|
1252
|
+
JobSearchUrgency2["PassivelyBrowsing"] = "passively_browsing";
|
|
1253
|
+
return JobSearchUrgency2;
|
|
1254
|
+
})(JobSearchUrgency || {});
|
|
1255
|
+
var SupportedJobSearchUrgencies = Object.values(JobSearchUrgency);
|
|
1256
|
+
var ReferralSource = /* @__PURE__ */ ((ReferralSource2) => {
|
|
1257
|
+
ReferralSource2["Recruiter"] = "recruiter";
|
|
1258
|
+
ReferralSource2["LinkedIn"] = "linkedin";
|
|
1259
|
+
ReferralSource2["TikTokInstagram"] = "tiktok_instagram";
|
|
1260
|
+
ReferralSource2["Google"] = "google";
|
|
1261
|
+
ReferralSource2["Reddit"] = "reddit";
|
|
1262
|
+
ReferralSource2["ChatGptGemini"] = "chatgpt_gemini";
|
|
1263
|
+
ReferralSource2["Friend"] = "friend";
|
|
1264
|
+
ReferralSource2["Other"] = "other";
|
|
1265
|
+
return ReferralSource2;
|
|
1266
|
+
})(ReferralSource || {});
|
|
1267
|
+
var SupportedReferralSources = Object.values(ReferralSource);
|
|
1268
|
+
var JobAlertFrequency = /* @__PURE__ */ ((JobAlertFrequency2) => {
|
|
1269
|
+
JobAlertFrequency2["Off"] = "off";
|
|
1270
|
+
JobAlertFrequency2["Daily"] = "daily";
|
|
1271
|
+
JobAlertFrequency2["Weekly"] = "weekly";
|
|
1272
|
+
return JobAlertFrequency2;
|
|
1273
|
+
})(JobAlertFrequency || {});
|
|
1274
|
+
var SupportedJobAlertFrequencies = Object.values(JobAlertFrequency);
|
|
1275
|
+
var NotificationType = /* @__PURE__ */ ((NotificationType2) => {
|
|
1276
|
+
NotificationType2["GroupActivity"] = "groupActivity";
|
|
1277
|
+
NotificationType2["JobActivity"] = "jobActivity";
|
|
1278
|
+
NotificationType2["ApplicationUpdates"] = "applicationUpdates";
|
|
1279
|
+
NotificationType2["ProductUpdates"] = "productUpdates";
|
|
1280
|
+
return NotificationType2;
|
|
1281
|
+
})(NotificationType || {});
|
|
1282
|
+
var SupportedNotificationTypes = Object.values(NotificationType);
|
|
1283
|
+
var SupportedSalaryCurrencies = ["USD", "EUR", "GBP", "SEK", "INR"];
|
|
1284
|
+
var MIN_SALARY_LOWER_BOUND = 0;
|
|
1285
|
+
var MIN_SALARY_UPPER_BOUND = 1e6;
|
|
1286
|
+
var PREFILL_MIRRORED_KEYS = [
|
|
1287
|
+
"headline",
|
|
1288
|
+
"aboutMe",
|
|
1289
|
+
"mobile",
|
|
1290
|
+
"location",
|
|
1291
|
+
"experienceLevel",
|
|
1292
|
+
"socialAccounts",
|
|
1293
|
+
"workExperiences",
|
|
1294
|
+
"educations"
|
|
1295
|
+
];
|
|
1296
|
+
var PRIVATE_PROFILE_FIELDS = [
|
|
1297
|
+
"email",
|
|
1298
|
+
"emailVerified",
|
|
1299
|
+
"mobile",
|
|
1300
|
+
"additionalInfo"
|
|
1301
|
+
];
|
|
1302
|
+
|
|
965
1303
|
// src/job/job-taxonomy.constant.ts
|
|
966
1304
|
var JobCategory = /* @__PURE__ */ ((JobCategory2) => {
|
|
967
1305
|
JobCategory2["TechnologyDataAndDigital"] = "technology_data_and_digital";
|
|
@@ -2029,6 +2367,185 @@ var TAXONOMY_LABELS = {
|
|
|
2029
2367
|
"fleet_management": "Fleet Management"
|
|
2030
2368
|
};
|
|
2031
2369
|
|
|
2370
|
+
// src/job/job-filter.schema.ts
|
|
2371
|
+
var GEO_GROUP = "Geo";
|
|
2372
|
+
var JobFilterShape = (0, import_yup21.object)({
|
|
2373
|
+
// ── Classification ──────────────────────────────────────────────────────────
|
|
2374
|
+
// Closed vocabularies, sent verbatim and matched exactly: a partial match
|
|
2375
|
+
// would make "remote" also match a hypothetical "remote_first".
|
|
2376
|
+
employmentType: enumFacet(SupportedEmploymentTypes, "Employment Type"),
|
|
2377
|
+
workMode: enumFacet(SupportedWorkModes, "Work Mode"),
|
|
2378
|
+
experienceLevel: enumFacet(SupportedExperienceLevels, "Experience Level"),
|
|
2379
|
+
educationLevel: enumFacet(SupportedEducationLevels, "Education Level"),
|
|
2380
|
+
status: enumFacet(SupportedJobStatuses, "Status"),
|
|
2381
|
+
// ── Taxonomy ────────────────────────────────────────────────────────────────
|
|
2382
|
+
// NOT `enumFacet`, deliberately. Taxonomy is stored as the ENGLISH LABEL
|
|
2383
|
+
// ("Technology, Data & Digital"), not the enum constant, so a caller sends the
|
|
2384
|
+
// label and constraining these to `SupportedJobCategories` would reject every
|
|
2385
|
+
// value that actually matches.
|
|
2386
|
+
//
|
|
2387
|
+
// `meta.options` publishes the vocabulary anyway, for forms only. The schema
|
|
2388
|
+
// still accepts any string — it must, per the note above — but an admin
|
|
2389
|
+
// picking one of eight categories should not be typing it: the stored label
|
|
2390
|
+
// is "Technology, Data & Digital", and a hand-typed "Technology" matches
|
|
2391
|
+
// nothing while looking correctly configured.
|
|
2392
|
+
category: tokens("Category").meta({ options: SupportedJobCategories }),
|
|
2393
|
+
industry: tokens("Industry").meta({ options: SupportedJobIndustries }),
|
|
2394
|
+
subCategories: tokens("Sub-category").meta({
|
|
2395
|
+
options: SupportedJobSubCategories
|
|
2396
|
+
}),
|
|
2397
|
+
tags: tokens("Tags"),
|
|
2398
|
+
// ── Identity ────────────────────────────────────────────────────────────────
|
|
2399
|
+
/**
|
|
2400
|
+
* Specific jobs, by `shortId`. The one facet that names jobs outright rather
|
|
2401
|
+
* than narrowing by an attribute of them.
|
|
2402
|
+
*
|
|
2403
|
+
* It exists for a HAND-PICKED set: a group whose jobs an admin chose
|
|
2404
|
+
* individually stores exactly this list as `group.jobIds`, so the field and
|
|
2405
|
+
* the facet share a name and the stored data needs no translation.
|
|
2406
|
+
*
|
|
2407
|
+
* Case is LOAD-BEARING and must not be normalised: a shortId is mixed case
|
|
2408
|
+
* (`tI8wnSIgRG5O`), so the uppercase treatment country and state codes get
|
|
2409
|
+
* would turn every lookup into a match on nothing.
|
|
2410
|
+
*/
|
|
2411
|
+
jobIds: tokens("Jobs"),
|
|
2412
|
+
// ── Skills and company ──────────────────────────────────────────────────────
|
|
2413
|
+
//
|
|
2414
|
+
// `widget` is a RENDERING hint for whoever builds a form from this schema: it
|
|
2415
|
+
// names the picker that produces values this facet can actually match, and
|
|
2416
|
+
// nothing validates differently because of it. A form generated without one
|
|
2417
|
+
// falls back to a free text box, which is what every admin filter was doing —
|
|
2418
|
+
// so "Angular" was typed where the stored skill is "angular" and the filter
|
|
2419
|
+
// narrowed to nothing while looking correctly configured.
|
|
2420
|
+
skills: tokens("Skills").meta({ widget: "skill" }),
|
|
2421
|
+
/** Company page SLUGS: a display name breaks the day a company renames itself. */
|
|
2422
|
+
companies: tokens("Companies").meta({ widget: "company" }),
|
|
2423
|
+
/**
|
|
2424
|
+
* Company display names, for callers that only have one. Prefer `companies`.
|
|
2425
|
+
*
|
|
2426
|
+
* `hidden`: not a second control in a generated form. An admin picking a
|
|
2427
|
+
* company gets the page, and `companies` (its slug) is the stable way to name
|
|
2428
|
+
* it — a display name stops matching the day a company renames itself. This
|
|
2429
|
+
* stays in the vocabulary for API callers who genuinely only hold a name.
|
|
2430
|
+
*/
|
|
2431
|
+
companyNames: tokens("Company Names").meta({ widget: "hidden" }),
|
|
2432
|
+
// ── Location ────────────────────────────────────────────────────────────────
|
|
2433
|
+
//
|
|
2434
|
+
// Four facets, THREE controls. They are independent OR-lists: jobs-service
|
|
2435
|
+
// matches each path separately, so `countryCodes` alone asks "anywhere in
|
|
2436
|
+
// India" and `cities` alone asks "in Pune, wherever that is". Collapsing them
|
|
2437
|
+
// into one object facet would lose exactly that, and lose the cross-entry
|
|
2438
|
+
// reading ("has an India office OR a London office") with it.
|
|
2439
|
+
//
|
|
2440
|
+
// What they must NOT be is free text. A country here is `IN`, not "India",
|
|
2441
|
+
// and a state code is a BARE `KA`, not `IN-KA` — type either long form and
|
|
2442
|
+
// the filter matches nothing while looking correctly configured.
|
|
2443
|
+
//
|
|
2444
|
+
// So each names the GEO reference autocomplete (common-service `/geo/*`, a
|
|
2445
|
+
// closed vocabulary) and the field of the picked row it stores. `geoLevel`
|
|
2446
|
+
// says which endpoint to search and `geoField` which key lands in the facet.
|
|
2447
|
+
//
|
|
2448
|
+
// THREE controls, not four: a picked state carries its name and its code, so
|
|
2449
|
+
// the old separate "State Codes" box asked twice for one fact. See `states`.
|
|
2450
|
+
//
|
|
2451
|
+
// Geoapify's `/locations` is NOT the vocabulary here. It geocodes free-text
|
|
2452
|
+
// addresses — the right tool for "where is this job", the wrong one for a
|
|
2453
|
+
// filter, since it is a paid call per keystroke and its results are open.
|
|
2454
|
+
/** ISO 3166-1 alpha-2, uppercase. */
|
|
2455
|
+
countryCodes: codeTokens("Countries").meta({
|
|
2456
|
+
group: GEO_GROUP,
|
|
2457
|
+
widget: "geo",
|
|
2458
|
+
geoLevel: "country",
|
|
2459
|
+
geoField: "countryCode"
|
|
2460
|
+
}),
|
|
2461
|
+
/** Stored BARE (`KA`, not `IN-KA`) — see @todo STATE-CODE-ISO-PREFIX. */
|
|
2462
|
+
stateCodes: codeTokens("States").meta({
|
|
2463
|
+
group: GEO_GROUP,
|
|
2464
|
+
widget: "geo",
|
|
2465
|
+
geoLevel: "state",
|
|
2466
|
+
geoField: "stateCode"
|
|
2467
|
+
}),
|
|
2468
|
+
/**
|
|
2469
|
+
* State NAMES, kept as a facet but WITHOUT a control of its own.
|
|
2470
|
+
*
|
|
2471
|
+
* There is no second box to fill: a picked state carries its name and its
|
|
2472
|
+
* code together, so asking an admin for both was asking twice for one fact.
|
|
2473
|
+
* The picker writes `stateCodes`, which is where the data is going.
|
|
2474
|
+
*
|
|
2475
|
+
* The facet stays because jobs still hold state names — 15,483 carry a name
|
|
2476
|
+
* with NO code — and a saved campaign may already constrain on it. Removing
|
|
2477
|
+
* it would silently drop that constraint from those campaigns. It becomes
|
|
2478
|
+
* unreachable from the UI and is settled by the data migration.
|
|
2479
|
+
*
|
|
2480
|
+
* @todo STATE-CODE-ISO-PREFIX — retire once every job carries a stateCode.
|
|
2481
|
+
*/
|
|
2482
|
+
states: tokens("States").meta({ group: GEO_GROUP, widget: "hidden" }),
|
|
2483
|
+
/**
|
|
2484
|
+
* City NAMES, and the one facet here that does not match on a code.
|
|
2485
|
+
*
|
|
2486
|
+
* A name is a display label, not a matching key: it is not unique (99 Indian
|
|
2487
|
+
* city names repeat in the reference data) and not stable (Bengaluru was
|
|
2488
|
+
* Bangalore). Every other facet here matches a code for exactly that reason.
|
|
2489
|
+
*
|
|
2490
|
+
* Cities cannot yet. There is no city code to match ON either side: the
|
|
2491
|
+
* reference dataset carries none, and no job stores one — `LocationSchema`
|
|
2492
|
+
* has country/state codes and a city NAME. A `cityCodes` facet would match
|
|
2493
|
+
* zero of 82,875 jobs on day one.
|
|
2494
|
+
*
|
|
2495
|
+
* So this stays on the name, and the picker does the work a code would: the
|
|
2496
|
+
* admin selects a real city from the reference data, so the stored string is
|
|
2497
|
+
* guaranteed to be a spelling that exists rather than a typo that silently
|
|
2498
|
+
* matches nothing.
|
|
2499
|
+
*
|
|
2500
|
+
* @todo CITY-CODE-UNLOCODE — the fix is UN/LOCODE (or IATA) as the city key,
|
|
2501
|
+
* and it is a data programme, not a facet change: ingest and reconcile the
|
|
2502
|
+
* codes against the 152,970 reference rows, add `cityCode` to
|
|
2503
|
+
* `LocationSchema`, backfill the ~81,000 jobs that carry only a name, and fix
|
|
2504
|
+
* the write paths that emit it. Only then can this match on a code.
|
|
2505
|
+
*/
|
|
2506
|
+
cities: tokens("Cities").meta({
|
|
2507
|
+
group: GEO_GROUP,
|
|
2508
|
+
widget: "geo",
|
|
2509
|
+
geoLevel: "city",
|
|
2510
|
+
geoField: "name"
|
|
2511
|
+
}),
|
|
2512
|
+
// ── Flags ───────────────────────────────────────────────────────────────────
|
|
2513
|
+
// No `.default(false)`: an unchecked box must mean "no constraint", not "only
|
|
2514
|
+
// the non-featured ones", so absent simply omits the clause.
|
|
2515
|
+
isFeatured: (0, import_yup21.boolean)().optional().label("Featured Only"),
|
|
2516
|
+
isPromoted: (0, import_yup21.boolean)().optional().label("Promoted Only"),
|
|
2517
|
+
featuredMarkets: tokens("Featured Markets"),
|
|
2518
|
+
// ── Ranges ──────────────────────────────────────────────────────────────────
|
|
2519
|
+
/**
|
|
2520
|
+
* Matched as an OVERLAP against the job's advertised band, not as a bound on
|
|
2521
|
+
* one end: `{min}` keeps jobs whose `salaryRange.max` reaches it.
|
|
2522
|
+
*/
|
|
2523
|
+
// NOT `currencyBound`: that guards the USER filter, where several currencies
|
|
2524
|
+
// can be selected and a bound is meaningless across them. Here the currency is
|
|
2525
|
+
// a single sibling field, so the bound is plain and the currency simply
|
|
2526
|
+
// narrows which jobs are compared.
|
|
2527
|
+
salary: (0, import_yup21.object)({
|
|
2528
|
+
min: (0, import_yup21.number)().integer().min(0).optional().default(void 0).label("Salary \u2265"),
|
|
2529
|
+
max: (0, import_yup21.number)().integer().min(0).optional().default(void 0).label("Salary \u2264"),
|
|
2530
|
+
currency: (0, import_yup21.string)().oneOf(SupportedSalaryCurrencies).optional().label("Currency")
|
|
2531
|
+
}).optional().default(void 0).label("Salary"),
|
|
2532
|
+
/** Posted within this many days. Relative, so a saved filter does not rot. */
|
|
2533
|
+
postedWithinDays: days("Posted Within (days)"),
|
|
2534
|
+
/**
|
|
2535
|
+
* Absolute posting window, for callers that want bounds rather than the
|
|
2536
|
+
* relative `postedWithinDays`. EPOCH MS, matching how the field is stored — a
|
|
2537
|
+
* coerced date string would not compare.
|
|
2538
|
+
*/
|
|
2539
|
+
createdAt: (0, import_yup21.object)({
|
|
2540
|
+
min: epoch("Posted After"),
|
|
2541
|
+
max: epoch("Posted Before")
|
|
2542
|
+
}).optional().default(void 0).label("Posted Between")
|
|
2543
|
+
}).label("Job Filter");
|
|
2544
|
+
var JobFilterSchema = rejectUnknownKeys(
|
|
2545
|
+
JobFilterShape,
|
|
2546
|
+
() => Object.keys(JobFilterShape.fields)
|
|
2547
|
+
);
|
|
2548
|
+
|
|
2032
2549
|
// src/job/job-taxonomy.util.ts
|
|
2033
2550
|
var INDUSTRY_OF_SUB = new Map(
|
|
2034
2551
|
JOB_TAXONOMY.flatMap(
|
|
@@ -2073,7 +2590,7 @@ function deriveIndustry(category, subCategories) {
|
|
|
2073
2590
|
}
|
|
2074
2591
|
|
|
2075
2592
|
// src/post/post.schema.ts
|
|
2076
|
-
var
|
|
2593
|
+
var import_yup22 = require("yup");
|
|
2077
2594
|
|
|
2078
2595
|
// src/post/post.constant.ts
|
|
2079
2596
|
var PostStatus = /* @__PURE__ */ ((PostStatus2) => {
|
|
@@ -2085,177 +2602,540 @@ var PostStatus = /* @__PURE__ */ ((PostStatus2) => {
|
|
|
2085
2602
|
var SupportedPostStatuses = Object.values(PostStatus);
|
|
2086
2603
|
|
|
2087
2604
|
// src/post/post.schema.ts
|
|
2088
|
-
var PostSchema = (0,
|
|
2605
|
+
var PostSchema = (0, import_yup22.object)({
|
|
2089
2606
|
/** Headline shown in listings and at the top of the post. */
|
|
2090
|
-
title: (0,
|
|
2607
|
+
title: (0, import_yup22.string)().required().min(5).max(200).label("Title"),
|
|
2091
2608
|
/** User-friendly, unique URL identifier. */
|
|
2092
|
-
slug: (0,
|
|
2609
|
+
slug: (0, import_yup22.string)().required().max(250).label("Slug"),
|
|
2093
2610
|
/** Short summary used in listings and meta description fallback. */
|
|
2094
|
-
excerpt: (0,
|
|
2611
|
+
excerpt: (0, import_yup22.string)().max(500).optional().label("Excerpt"),
|
|
2095
2612
|
/** Rendered HTML source of truth for the post body (sanitized on write/render).
|
|
2096
2613
|
* Supports rich formatting markdown can't express (image size/alignment, etc). */
|
|
2097
|
-
bodyHTML: (0,
|
|
2614
|
+
bodyHTML: (0, import_yup22.string)().required().min(1).label("Body (HTML)"),
|
|
2098
2615
|
/** Optional Markdown mirror of the body (portable text / search fallback).
|
|
2099
2616
|
* Kept for back-compat and plain-text derivation; bodyHTML is authoritative. */
|
|
2100
|
-
bodyMarkdown: (0,
|
|
2617
|
+
bodyMarkdown: (0, import_yup22.string)().optional().label("Body (Markdown)"),
|
|
2101
2618
|
/** Cover/hero image URL. Not `.url()`-validated: the value is either an
|
|
2102
2619
|
* uploaded file URL served by this platform (which may be a bare host like
|
|
2103
2620
|
* `http://localhost:3082/...` in dev, that yup's `.url()` wrongly rejects) or
|
|
2104
2621
|
* an admin-pasted link. Kept as a plain string; empty ⇒ null. */
|
|
2105
|
-
coverImage: (0,
|
|
2622
|
+
coverImage: (0, import_yup22.string)().nullable().optional().label("Cover image"),
|
|
2106
2623
|
/** Author (user id). */
|
|
2107
|
-
authorId: (0,
|
|
2624
|
+
authorId: (0, import_yup22.string)().required().label("Author ID"),
|
|
2108
2625
|
/** Free-form tags for filtering and related-posts. */
|
|
2109
|
-
tags: (0,
|
|
2626
|
+
tags: (0, import_yup22.array)().of((0, import_yup22.string)().trim().required()).default([]).label("Tags"),
|
|
2110
2627
|
/** Estimated reading time in minutes (server-derived from body length). */
|
|
2111
|
-
readingTimeMinutes: (0,
|
|
2628
|
+
readingTimeMinutes: (0, import_yup22.number)().min(0).optional().label("Reading time (minutes)"),
|
|
2112
2629
|
/** SEO overrides. */
|
|
2113
|
-
seoTags: (0,
|
|
2114
|
-
description: (0,
|
|
2115
|
-
keywords: (0,
|
|
2630
|
+
seoTags: (0, import_yup22.object)({
|
|
2631
|
+
description: (0, import_yup22.string)().optional().label("Description"),
|
|
2632
|
+
keywords: (0, import_yup22.array)().of((0, import_yup22.string)().required()).optional().label("Keywords")
|
|
2116
2633
|
}).nullable().optional().label("SEO Tags"),
|
|
2117
2634
|
/** When the post was first published (epoch ms). Server-owned. */
|
|
2118
|
-
publishedAt: (0,
|
|
2635
|
+
publishedAt: (0, import_yup22.number)().nullable().optional().label("Published at"),
|
|
2119
2636
|
/** Whether the post is featured in the blog listing. */
|
|
2120
|
-
isFeatured: (0,
|
|
2637
|
+
isFeatured: (0, import_yup22.boolean)().default(false).optional().label("Is featured"),
|
|
2121
2638
|
/** Publication state. */
|
|
2122
|
-
status: (0,
|
|
2639
|
+
status: (0, import_yup22.string)().oneOf(SupportedPostStatuses).default("draft" /* Draft */).required().label("Status")
|
|
2123
2640
|
}).concat(DbDefaultSchema).noUnknown().label("Post");
|
|
2124
2641
|
|
|
2125
|
-
// src/
|
|
2126
|
-
var
|
|
2642
|
+
// src/geo/geo.constant.ts
|
|
2643
|
+
var GeoKind = /* @__PURE__ */ ((GeoKind2) => {
|
|
2644
|
+
GeoKind2["Country"] = "country";
|
|
2645
|
+
GeoKind2["State"] = "state";
|
|
2646
|
+
GeoKind2["City"] = "city";
|
|
2647
|
+
return GeoKind2;
|
|
2648
|
+
})(GeoKind || {});
|
|
2649
|
+
var SupportedGeoKinds = Object.values(GeoKind);
|
|
2650
|
+
var GeoSyncStatus = /* @__PURE__ */ ((GeoSyncStatus3) => {
|
|
2651
|
+
GeoSyncStatus3["Pending"] = "pending";
|
|
2652
|
+
GeoSyncStatus3["Running"] = "running";
|
|
2653
|
+
GeoSyncStatus3["Succeeded"] = "succeeded";
|
|
2654
|
+
GeoSyncStatus3["Failed"] = "failed";
|
|
2655
|
+
GeoSyncStatus3["Skipped"] = "skipped";
|
|
2656
|
+
return GeoSyncStatus3;
|
|
2657
|
+
})(GeoSyncStatus || {});
|
|
2658
|
+
var SupportedGeoSyncStatuses = Object.values(GeoSyncStatus);
|
|
2659
|
+
var TerminalGeoSyncStatuses = [
|
|
2660
|
+
"succeeded" /* Succeeded */,
|
|
2661
|
+
"failed" /* Failed */,
|
|
2662
|
+
"skipped" /* Skipped */
|
|
2663
|
+
];
|
|
2664
|
+
var GeoSyncPhase = /* @__PURE__ */ ((GeoSyncPhase3) => {
|
|
2665
|
+
GeoSyncPhase3["Checking"] = "checking";
|
|
2666
|
+
GeoSyncPhase3["Countries"] = "countries";
|
|
2667
|
+
GeoSyncPhase3["States"] = "states";
|
|
2668
|
+
GeoSyncPhase3["Cities"] = "cities";
|
|
2669
|
+
GeoSyncPhase3["Indexing"] = "indexing";
|
|
2670
|
+
GeoSyncPhase3["Done"] = "done";
|
|
2671
|
+
return GeoSyncPhase3;
|
|
2672
|
+
})(GeoSyncPhase || {});
|
|
2673
|
+
var SupportedGeoSyncPhases = Object.values(GeoSyncPhase);
|
|
2674
|
+
var GeoSyncTrigger = /* @__PURE__ */ ((GeoSyncTrigger3) => {
|
|
2675
|
+
GeoSyncTrigger3["Manual"] = "manual";
|
|
2676
|
+
GeoSyncTrigger3["Boot"] = "boot";
|
|
2677
|
+
return GeoSyncTrigger3;
|
|
2678
|
+
})(GeoSyncTrigger || {});
|
|
2679
|
+
var SupportedGeoSyncTriggers = Object.values(GeoSyncTrigger);
|
|
2680
|
+
var GEO_COLLECTIONS = {
|
|
2681
|
+
countries: "geo_countries",
|
|
2682
|
+
states: "geo_states",
|
|
2683
|
+
cities: "geo_cities",
|
|
2684
|
+
syncRuns: "geo_sync_runs"
|
|
2685
|
+
};
|
|
2686
|
+
var GEO_SEARCH_MAX = 64;
|
|
2127
2687
|
|
|
2128
|
-
// src/
|
|
2129
|
-
var
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2688
|
+
// src/geo/country.schema.ts
|
|
2689
|
+
var import_yup23 = require("yup");
|
|
2690
|
+
var CountrySchema = (0, import_yup23.object)({
|
|
2691
|
+
/** The upstream dr5hn id. Stable, and the key every upsert matches on. */
|
|
2692
|
+
id: (0, import_yup23.number)().integer().required().label("ID"),
|
|
2693
|
+
name: (0, import_yup23.string)().trim().required().label("Name"),
|
|
2694
|
+
/**
|
|
2695
|
+
* ISO 3166-1 alpha-2, UPPERCASE (`IN`, `SE`).
|
|
2696
|
+
*
|
|
2697
|
+
* Uppercased on ingest, because every filter facet in this package
|
|
2698
|
+
* uppercase-normalizes codes (`codeTokens`) and stored job/user documents are
|
|
2699
|
+
* already clean uppercase alpha-2. A lowercase value here would compare
|
|
2700
|
+
* unequal to all of them and silently match nothing.
|
|
2701
|
+
*/
|
|
2702
|
+
countryCode: (0, import_yup23.string)().trim().uppercase().length(2).required().label("Country Code"),
|
|
2703
|
+
iso3: (0, import_yup23.string)().trim().uppercase().length(3).optional().nullable().label("ISO3"),
|
|
2704
|
+
numericCode: (0, import_yup23.string)().trim().optional().nullable().label("Numeric Code"),
|
|
2705
|
+
phonecode: (0, import_yup23.string)().trim().optional().nullable().label("Phone Code"),
|
|
2706
|
+
capital: (0, import_yup23.string)().trim().optional().nullable().label("Capital"),
|
|
2707
|
+
currency: (0, import_yup23.string)().trim().optional().nullable().label("Currency"),
|
|
2708
|
+
currencyName: (0, import_yup23.string)().trim().optional().nullable().label("Currency Name"),
|
|
2709
|
+
currencySymbol: (0, import_yup23.string)().trim().optional().nullable().label("Currency Symbol"),
|
|
2710
|
+
tld: (0, import_yup23.string)().trim().optional().nullable().label("TLD"),
|
|
2711
|
+
/** The name in its own script, e.g. "भारत". Stored, but NOT searchable; see `searchByLocale`. */
|
|
2712
|
+
native: (0, import_yup23.string)().trim().optional().nullable().label("Native Name"),
|
|
2713
|
+
population: (0, import_yup23.number)().optional().nullable().label("Population"),
|
|
2714
|
+
gdp: (0, import_yup23.number)().optional().nullable().label("GDP"),
|
|
2715
|
+
region: (0, import_yup23.string)().trim().optional().nullable().label("Region"),
|
|
2716
|
+
regionId: (0, import_yup23.number)().integer().optional().nullable().label("Region ID"),
|
|
2717
|
+
subregion: (0, import_yup23.string)().trim().optional().nullable().label("Subregion"),
|
|
2718
|
+
subregionId: (0, import_yup23.number)().integer().optional().nullable().label("Subregion ID"),
|
|
2719
|
+
nationality: (0, import_yup23.string)().trim().optional().nullable().label("Nationality"),
|
|
2720
|
+
areaSqKm: (0, import_yup23.number)().optional().nullable().label("Area (sq km)"),
|
|
2721
|
+
postalCodeFormat: (0, import_yup23.string)().trim().optional().nullable().label("Postal Code Format"),
|
|
2722
|
+
postalCodeRegex: (0, import_yup23.string)().optional().nullable().label("Postal Code Regex"),
|
|
2723
|
+
timezones: (0, import_yup23.array)().of(
|
|
2724
|
+
(0, import_yup23.object)({
|
|
2725
|
+
zoneName: (0, import_yup23.string)().optional().nullable().label("Zone Name"),
|
|
2726
|
+
gmtOffset: (0, import_yup23.number)().optional().nullable().label("GMT Offset"),
|
|
2727
|
+
gmtOffsetName: (0, import_yup23.string)().optional().nullable().label("GMT Offset Name"),
|
|
2728
|
+
abbreviation: (0, import_yup23.string)().optional().nullable().label("Abbreviation"),
|
|
2729
|
+
tzName: (0, import_yup23.string)().optional().nullable().label("TZ Name")
|
|
2730
|
+
})
|
|
2731
|
+
).optional().nullable().label("Timezones"),
|
|
2732
|
+
/**
|
|
2733
|
+
* Localized names keyed by locale (`{ sv: "Indien", fr: "Inde", … }`), ~19 of
|
|
2734
|
+
* them. Deliberately UNSHAPED: the key set is upstream's to change, and
|
|
2735
|
+
* declaring it here would turn a new locale into a validation failure. The
|
|
2736
|
+
* VALUES are split per-locale into `searchByLocale`.
|
|
2737
|
+
*/
|
|
2738
|
+
translations: (0, import_yup23.object)().optional().nullable().label("Translations"),
|
|
2739
|
+
/**
|
|
2740
|
+
* Coordinates as NUMBERS.
|
|
2741
|
+
*
|
|
2742
|
+
* Upstream ships these as strings (`"33.00000000"`). They are coerced on
|
|
2743
|
+
* ingest, because a range query against a string compares lexicographically:
|
|
2744
|
+
* `"9" > "10"` is true, so a bounding-box filter would be quietly wrong rather
|
|
2745
|
+
* than failing loudly.
|
|
2746
|
+
*/
|
|
2747
|
+
latitude: (0, import_yup23.number)().optional().nullable().label("Latitude"),
|
|
2748
|
+
longitude: (0, import_yup23.number)().optional().nullable().label("Longitude"),
|
|
2749
|
+
emoji: (0, import_yup23.string)().optional().nullable().label("Emoji"),
|
|
2750
|
+
emojiU: (0, import_yup23.string)().optional().nullable().label("Emoji Unicode"),
|
|
2751
|
+
/**
|
|
2752
|
+
* The name field searched for the DEFAULT (English) locale: the canonical
|
|
2753
|
+
* `name`, and nothing else.
|
|
2754
|
+
*
|
|
2755
|
+
* Search is scoped to ONE locale, so the searchable names are split across
|
|
2756
|
+
* this field and `searchByLocale` rather than pooled into one array. A pooled
|
|
2757
|
+
* field would make every locale's search implicitly a search of all 19
|
|
2758
|
+
* languages: a French UI searching "Inde" would also match German "Indien".
|
|
2759
|
+
*/
|
|
2760
|
+
searchDefault: (0, import_yup23.array)().of((0, import_yup23.string)().required()).optional().default([]).label("Search Default"),
|
|
2761
|
+
/**
|
|
2762
|
+
* Per-locale name fields, keyed by lowercased BCP-47 tag (`de`, `pt-br`).
|
|
2763
|
+
* Each carries ONLY that locale's translation, which is what makes
|
|
2764
|
+
* `?locale=de` German and not "German plus everything that looks similar".
|
|
2765
|
+
*
|
|
2766
|
+
* Unshaped for the same reason as `translations`: the key set is upstream's.
|
|
2767
|
+
*
|
|
2768
|
+
* `native` is deliberately NOT indexed here. It cannot be attributed to a
|
|
2769
|
+
* language with this data — 84% of city native names equal SEVERAL locales'
|
|
2770
|
+
* translations (Latin spellings coinciding across de/it/nl/hr/pl) while
|
|
2771
|
+
* distinctive non-Latin names like `भारत` match none — so filing `कर्नाटक`
|
|
2772
|
+
* under German would be worse than not indexing it at all.
|
|
2773
|
+
*
|
|
2774
|
+
* The 19 upstream locales are br, ko, pt-BR, pt, nl, hr, fa, de, es, fr, ja,
|
|
2775
|
+
* it, zh-CN, tr, ru, uk, pl, hi, ar. **`sv` is absent**, so a Swedish search
|
|
2776
|
+
* matches nothing. That is the accepted cost of strict scoping.
|
|
2777
|
+
*/
|
|
2778
|
+
searchByLocale: (0, import_yup23.object)().optional().nullable().label("Search By Locale"),
|
|
2779
|
+
/** Epoch ms of the run that last wrote this row. Drives the stale-row prune. */
|
|
2780
|
+
syncedAt: (0, import_yup23.number)().optional().nullable().label("Synced At")
|
|
2781
|
+
}).label("Country");
|
|
2782
|
+
|
|
2783
|
+
// src/geo/state.schema.ts
|
|
2784
|
+
var import_yup24 = require("yup");
|
|
2785
|
+
var StateSchema = (0, import_yup24.object)({
|
|
2786
|
+
/** The upstream dr5hn id. Stable, and the key every upsert matches on. */
|
|
2787
|
+
id: (0, import_yup24.number)().integer().required().label("ID"),
|
|
2788
|
+
name: (0, import_yup24.string)().trim().required().label("Name"),
|
|
2789
|
+
countryId: (0, import_yup24.number)().integer().optional().nullable().label("Country ID"),
|
|
2790
|
+
/** ISO 3166-1 alpha-2 of the parent, UPPERCASE. See `CountrySchema.countryCode`. */
|
|
2791
|
+
countryCode: (0, import_yup24.string)().trim().uppercase().length(2).required().label("Country Code"),
|
|
2792
|
+
countryName: (0, import_yup24.string)().trim().optional().nullable().label("Country Name"),
|
|
2793
|
+
/**
|
|
2794
|
+
* The BARE subdivision code, e.g. `KA` (upstream `iso2`).
|
|
2795
|
+
*
|
|
2796
|
+
* `@todo STATE-CODE-ISO-PREFIX` — this is **not globally unique**: `BDS`, `CA`,
|
|
2797
|
+
* `NL` and `SP` all recur across several countries, so it is only meaningful
|
|
2798
|
+
* PAIRED with `countryCode`. That is why the cities route is nested under both
|
|
2799
|
+
* codes rather than under a state code alone, which would silently serve
|
|
2800
|
+
* another country's cities. `stateCodeIso` below is the migration target;
|
|
2801
|
+
* both are stored so the migration has its destination present before any
|
|
2802
|
+
* consumer moves.
|
|
2803
|
+
*/
|
|
2804
|
+
stateCode: (0, import_yup24.string)().trim().uppercase().optional().nullable().label("State Code"),
|
|
2805
|
+
/** Fully-qualified ISO 3166-2, e.g. `IN-KA` (upstream `iso3166_2`). */
|
|
2806
|
+
stateCodeIso: (0, import_yup24.string)().trim().uppercase().optional().nullable().label("ISO 3166-2 Code"),
|
|
2807
|
+
fipsCode: (0, import_yup24.string)().trim().optional().nullable().label("FIPS Code"),
|
|
2808
|
+
/** Upstream's own word for the subdivision: "province", "state", "district", … */
|
|
2809
|
+
type: (0, import_yup24.string)().trim().optional().nullable().label("Type"),
|
|
2810
|
+
level: (0, import_yup24.number)().integer().optional().nullable().label("Level"),
|
|
2811
|
+
parentId: (0, import_yup24.number)().integer().optional().nullable().label("Parent ID"),
|
|
2812
|
+
/** The name in its own script. Stored, but NOT searchable; see `searchByLocale`. */
|
|
2813
|
+
native: (0, import_yup24.string)().trim().optional().nullable().label("Native Name"),
|
|
2814
|
+
/** Numbers, not the strings upstream ships. See `CountrySchema.latitude`. */
|
|
2815
|
+
latitude: (0, import_yup24.number)().optional().nullable().label("Latitude"),
|
|
2816
|
+
longitude: (0, import_yup24.number)().optional().nullable().label("Longitude"),
|
|
2817
|
+
timezone: (0, import_yup24.string)().trim().optional().nullable().label("Timezone"),
|
|
2818
|
+
/** Localized names keyed by locale. Unshaped; see `CountrySchema.translations`. */
|
|
2819
|
+
translations: (0, import_yup24.object)().optional().nullable().label("Translations"),
|
|
2820
|
+
/**
|
|
2821
|
+
* The name field searched for the DEFAULT (English) locale: the canonical
|
|
2822
|
+
* `name`, and nothing else.
|
|
2823
|
+
*
|
|
2824
|
+
* Search is scoped to ONE locale, so the searchable names are split across
|
|
2825
|
+
* this field and `searchByLocale` rather than pooled into one array. A pooled
|
|
2826
|
+
* field would make every locale's search implicitly a search of all 19
|
|
2827
|
+
* languages: a French UI searching "Inde" would also match German "Indien".
|
|
2828
|
+
*/
|
|
2829
|
+
searchDefault: (0, import_yup24.array)().of((0, import_yup24.string)().required()).optional().default([]).label("Search Default"),
|
|
2830
|
+
/**
|
|
2831
|
+
* Per-locale name fields, keyed by lowercased BCP-47 tag (`de`, `pt-br`).
|
|
2832
|
+
* Each carries ONLY that locale's translation, which is what makes
|
|
2833
|
+
* `?locale=de` German and not "German plus everything that looks similar".
|
|
2834
|
+
*
|
|
2835
|
+
* Unshaped for the same reason as `translations`: the key set is upstream's.
|
|
2836
|
+
*
|
|
2837
|
+
* `native` is deliberately NOT indexed here. It cannot be attributed to a
|
|
2838
|
+
* language with this data — 84% of city native names equal SEVERAL locales'
|
|
2839
|
+
* translations (Latin spellings coinciding across de/it/nl/hr/pl) while
|
|
2840
|
+
* distinctive non-Latin names like `भारत` match none — so filing `कर्नाटक`
|
|
2841
|
+
* under German would be worse than not indexing it at all.
|
|
2842
|
+
*
|
|
2843
|
+
* The 19 upstream locales are br, ko, pt-BR, pt, nl, hr, fa, de, es, fr, ja,
|
|
2844
|
+
* it, zh-CN, tr, ru, uk, pl, hi, ar. **`sv` is absent**, so a Swedish search
|
|
2845
|
+
* matches nothing. That is the accepted cost of strict scoping.
|
|
2846
|
+
*/
|
|
2847
|
+
searchByLocale: (0, import_yup24.object)().optional().nullable().label("Search By Locale"),
|
|
2848
|
+
/** Epoch ms of the run that last wrote this row. Drives the stale-row prune. */
|
|
2849
|
+
syncedAt: (0, import_yup24.number)().optional().nullable().label("Synced At")
|
|
2850
|
+
}).label("State");
|
|
2851
|
+
|
|
2852
|
+
// src/geo/city.schema.ts
|
|
2853
|
+
var import_yup25 = require("yup");
|
|
2854
|
+
var CitySchema = (0, import_yup25.object)({
|
|
2855
|
+
/** The upstream dr5hn id. Stable, and the key every upsert matches on. */
|
|
2856
|
+
id: (0, import_yup25.number)().integer().required().label("ID"),
|
|
2857
|
+
name: (0, import_yup25.string)().trim().required().label("Name"),
|
|
2858
|
+
stateId: (0, import_yup25.number)().integer().optional().nullable().label("State ID"),
|
|
2859
|
+
/** The BARE subdivision code. Only meaningful paired with `countryCode`; see `StateSchema`. */
|
|
2860
|
+
stateCode: (0, import_yup25.string)().trim().uppercase().optional().nullable().label("State Code"),
|
|
2861
|
+
/**
|
|
2862
|
+
* Fully-qualified ISO 3166-2, e.g. `IN-KA`.
|
|
2863
|
+
*
|
|
2864
|
+
* DERIVED on ingest as `${countryCode}-${stateCode}`, because the upstream city
|
|
2865
|
+
* record carries only the bare `state_code` — unlike the state record, which
|
|
2866
|
+
* ships `iso3166_2` directly. Stored anyway so the `@todo STATE-CODE-ISO-PREFIX`
|
|
2867
|
+
* migration can re-point the cities route without a 153k-row re-ingest.
|
|
2868
|
+
* Null when either half is missing.
|
|
2869
|
+
*/
|
|
2870
|
+
stateCodeIso: (0, import_yup25.string)().trim().uppercase().optional().nullable().label("ISO 3166-2 Code"),
|
|
2871
|
+
stateName: (0, import_yup25.string)().trim().optional().nullable().label("State Name"),
|
|
2872
|
+
countryId: (0, import_yup25.number)().integer().optional().nullable().label("Country ID"),
|
|
2873
|
+
/** ISO 3166-1 alpha-2 of the parent, UPPERCASE. See `CountrySchema.countryCode`. */
|
|
2874
|
+
countryCode: (0, import_yup25.string)().trim().uppercase().length(2).required().label("Country Code"),
|
|
2875
|
+
countryName: (0, import_yup25.string)().trim().optional().nullable().label("Country Name"),
|
|
2876
|
+
/** Numbers, not the strings upstream ships. See `CountrySchema.latitude`. */
|
|
2877
|
+
latitude: (0, import_yup25.number)().optional().nullable().label("Latitude"),
|
|
2878
|
+
longitude: (0, import_yup25.number)().optional().nullable().label("Longitude"),
|
|
2879
|
+
/** The name in its own script. Stored, but NOT searchable; see `searchByLocale`. */
|
|
2880
|
+
native: (0, import_yup25.string)().trim().optional().nullable().label("Native Name"),
|
|
2881
|
+
type: (0, import_yup25.string)().trim().optional().nullable().label("Type"),
|
|
2882
|
+
level: (0, import_yup25.number)().integer().optional().nullable().label("Level"),
|
|
2883
|
+
parentId: (0, import_yup25.number)().integer().optional().nullable().label("Parent ID"),
|
|
2884
|
+
/**
|
|
2885
|
+
* Null on most rows. Worth keeping (it is a good relevance signal when
|
|
2886
|
+
* present) but never worth SORTING by alone, since a null-heavy sort buries
|
|
2887
|
+
* every city upstream has no figure for.
|
|
2888
|
+
*/
|
|
2889
|
+
population: (0, import_yup25.number)().optional().nullable().label("Population"),
|
|
2890
|
+
timezone: (0, import_yup25.string)().trim().optional().nullable().label("Timezone"),
|
|
2891
|
+
/** Localized names keyed by locale. Unshaped; see `CountrySchema.translations`. */
|
|
2892
|
+
translations: (0, import_yup25.object)().optional().nullable().label("Translations"),
|
|
2893
|
+
/**
|
|
2894
|
+
* The name field searched for the DEFAULT (English) locale: the canonical
|
|
2895
|
+
* `name`, and nothing else.
|
|
2896
|
+
*
|
|
2897
|
+
* Search is scoped to ONE locale, so the searchable names are split across
|
|
2898
|
+
* this field and `searchByLocale` rather than pooled into one array. A pooled
|
|
2899
|
+
* field would make every locale's search implicitly a search of all 19
|
|
2900
|
+
* languages: a French UI searching "Inde" would also match German "Indien".
|
|
2901
|
+
*/
|
|
2902
|
+
searchDefault: (0, import_yup25.array)().of((0, import_yup25.string)().required()).optional().default([]).label("Search Default"),
|
|
2903
|
+
/**
|
|
2904
|
+
* Per-locale name fields, keyed by lowercased BCP-47 tag (`de`, `pt-br`).
|
|
2905
|
+
* Each carries ONLY that locale's translation, which is what makes
|
|
2906
|
+
* `?locale=de` German and not "German plus everything that looks similar".
|
|
2907
|
+
*
|
|
2908
|
+
* Unshaped for the same reason as `translations`: the key set is upstream's.
|
|
2909
|
+
*
|
|
2910
|
+
* `native` is deliberately NOT indexed here. It cannot be attributed to a
|
|
2911
|
+
* language with this data — 84% of city native names equal SEVERAL locales'
|
|
2912
|
+
* translations (Latin spellings coinciding across de/it/nl/hr/pl) while
|
|
2913
|
+
* distinctive non-Latin names like `भारत` match none — so filing `कर्नाटक`
|
|
2914
|
+
* under German would be worse than not indexing it at all.
|
|
2915
|
+
*
|
|
2916
|
+
* The 19 upstream locales are br, ko, pt-BR, pt, nl, hr, fa, de, es, fr, ja,
|
|
2917
|
+
* it, zh-CN, tr, ru, uk, pl, hi, ar. **`sv` is absent**, so a Swedish search
|
|
2918
|
+
* matches nothing. That is the accepted cost of strict scoping.
|
|
2919
|
+
*/
|
|
2920
|
+
searchByLocale: (0, import_yup25.object)().optional().nullable().label("Search By Locale"),
|
|
2921
|
+
/** Epoch ms of the run that last wrote this row. Drives the stale-row prune. */
|
|
2922
|
+
syncedAt: (0, import_yup25.number)().optional().nullable().label("Synced At")
|
|
2923
|
+
}).label("City");
|
|
2924
|
+
|
|
2925
|
+
// src/geo/country-filter.schema.ts
|
|
2926
|
+
var import_yup26 = require("yup");
|
|
2927
|
+
var CountryFilterShape = (0, import_yup26.object)({
|
|
2928
|
+
/** ISO 3166-1 alpha-2 codes, e.g. `IN`. Uppercased, matching how they are stored. */
|
|
2929
|
+
countryCodes: codeTokens("Country Codes"),
|
|
2930
|
+
/** ISO 3166-1 alpha-3 codes, e.g. `IND`. */
|
|
2931
|
+
iso3s: codeTokens("ISO3 Codes"),
|
|
2932
|
+
/**
|
|
2933
|
+
* `tokens`, NOT `codeTokens`: a region is a display string ("Asia", "Europe"),
|
|
2934
|
+
* and uppercasing it would match nothing.
|
|
2935
|
+
*/
|
|
2936
|
+
regions: tokens("Regions"),
|
|
2937
|
+
subregions: tokens("Subregions"),
|
|
2938
|
+
/** Display names, matched exactly. Free-text matching is `?search=`. */
|
|
2939
|
+
names: tokens("Names")
|
|
2940
|
+
}).label("Country Filter");
|
|
2941
|
+
var CountryFilterSchema = rejectUnknownKeys(
|
|
2942
|
+
CountryFilterShape,
|
|
2943
|
+
() => Object.keys(CountryFilterShape.fields)
|
|
2159
2944
|
);
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
var
|
|
2228
|
-
|
|
2229
|
-
"
|
|
2230
|
-
"
|
|
2231
|
-
|
|
2232
|
-
"
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
"
|
|
2241
|
-
|
|
2242
|
-
"
|
|
2243
|
-
|
|
2945
|
+
|
|
2946
|
+
// src/geo/state-filter.schema.ts
|
|
2947
|
+
var import_yup27 = require("yup");
|
|
2948
|
+
var StateFilterShape = (0, import_yup27.object)({
|
|
2949
|
+
/** ISO 3166-1 alpha-2 of the parent country, e.g. `IN`. */
|
|
2950
|
+
countryCodes: codeTokens("Country Codes"),
|
|
2951
|
+
/**
|
|
2952
|
+
* BARE subdivision codes, e.g. `KA`.
|
|
2953
|
+
*
|
|
2954
|
+
* `@todo STATE-CODE-ISO-PREFIX` — not globally unique, so this facet ALONE is
|
|
2955
|
+
* an ambiguous question: `stateCodes[0]=BDS` matches Badakhshan in Afghanistan
|
|
2956
|
+
* and every other `BDS` elsewhere. Pair it with `countryCodes` to mean one
|
|
2957
|
+
* place. See `StateSchema.stateCode`.
|
|
2958
|
+
*/
|
|
2959
|
+
stateCodes: codeTokens("State Codes"),
|
|
2960
|
+
/** Display names, matched exactly. Free-text matching is `?search=`. */
|
|
2961
|
+
names: tokens("Names"),
|
|
2962
|
+
/**
|
|
2963
|
+
* Upstream's own subdivision word: "province", "state", "district", …
|
|
2964
|
+
*
|
|
2965
|
+
* `tokens`, NOT `codeTokens`: these are lowercase display strings and
|
|
2966
|
+
* uppercasing them would match nothing. An OPEN vocabulary rather than an
|
|
2967
|
+
* `enumFacet`, because upstream ships 30-odd values and adding one should not
|
|
2968
|
+
* require a schema release here.
|
|
2969
|
+
*/
|
|
2970
|
+
types: tokens("Types")
|
|
2971
|
+
}).label("State Filter");
|
|
2972
|
+
var StateFilterSchema = rejectUnknownKeys(
|
|
2973
|
+
StateFilterShape,
|
|
2974
|
+
() => Object.keys(StateFilterShape.fields)
|
|
2975
|
+
);
|
|
2976
|
+
|
|
2977
|
+
// src/geo/city-filter.schema.ts
|
|
2978
|
+
var import_yup28 = require("yup");
|
|
2979
|
+
var CityFilterShape = (0, import_yup28.object)({
|
|
2980
|
+
/** ISO 3166-1 alpha-2 of the parent country, e.g. `IN`. */
|
|
2981
|
+
countryCodes: codeTokens("Country Codes"),
|
|
2982
|
+
/**
|
|
2983
|
+
* BARE subdivision codes, e.g. `KA`. Ambiguous without `countryCodes`; see
|
|
2984
|
+
* `StateFilterSchema.stateCodes` and `@todo STATE-CODE-ISO-PREFIX`.
|
|
2985
|
+
*/
|
|
2986
|
+
stateCodes: codeTokens("State Codes"),
|
|
2987
|
+
/** Display names, matched exactly. Free-text matching is `?search=`. */
|
|
2988
|
+
names: tokens("Names")
|
|
2989
|
+
}).label("City Filter");
|
|
2990
|
+
var CityFilterSchema = rejectUnknownKeys(
|
|
2991
|
+
CityFilterShape,
|
|
2992
|
+
() => Object.keys(CityFilterShape.fields)
|
|
2993
|
+
);
|
|
2994
|
+
|
|
2995
|
+
// src/geo/geo-sync.schema.ts
|
|
2996
|
+
var import_yup29 = require("yup");
|
|
2997
|
+
var GeoSyncCountsSchema = (0, import_yup29.object)({
|
|
2998
|
+
read: (0, import_yup29.number)().integer().min(0).default(0).label("Read"),
|
|
2999
|
+
upserted: (0, import_yup29.number)().integer().min(0).default(0).label("Upserted"),
|
|
3000
|
+
modified: (0, import_yup29.number)().integer().min(0).default(0).label("Modified"),
|
|
3001
|
+
matched: (0, import_yup29.number)().integer().min(0).default(0).label("Matched"),
|
|
3002
|
+
/** Rows removed because upstream dropped them. See `prunedSkipped`. */
|
|
3003
|
+
pruned: (0, import_yup29.number)().integer().min(0).default(0).label("Pruned"),
|
|
3004
|
+
/**
|
|
3005
|
+
* True when the stale-row prune was REFUSED because it would have removed more
|
|
3006
|
+
* than its safety threshold of the collection. A partial fetch that looked
|
|
3007
|
+
* like a success must not be able to empty the table, so the run reports the
|
|
3008
|
+
* refusal rather than acting on it.
|
|
3009
|
+
*/
|
|
3010
|
+
prunedSkipped: (0, import_yup29.boolean)().default(false).label("Prune Skipped")
|
|
3011
|
+
}).label("Sync Counts");
|
|
3012
|
+
var GeoSyncRunSchema = (0, import_yup29.object)({
|
|
3013
|
+
jobId: (0, import_yup29.string)().required().label("Job ID"),
|
|
3014
|
+
status: (0, import_yup29.mixed)().oneOf(SupportedGeoSyncStatuses).required().label("Status"),
|
|
3015
|
+
trigger: (0, import_yup29.mixed)().oneOf(SupportedGeoSyncTriggers).optional().label("Trigger"),
|
|
3016
|
+
/** Whether the caller asked to run even though the release tag was unchanged. */
|
|
3017
|
+
force: (0, import_yup29.boolean)().default(false).label("Force"),
|
|
3018
|
+
/**
|
|
3019
|
+
* The upstream release tag this run fetched, e.g. `v3.2-export.7`.
|
|
3020
|
+
*
|
|
3021
|
+
* Null only when the tag could not be read AND `force` was set — a run that
|
|
3022
|
+
* cannot tell what version it is fetching is otherwise a failure, not a
|
|
3023
|
+
* refresh, so it is not allowed to proceed unattended.
|
|
3024
|
+
*/
|
|
3025
|
+
releaseTag: (0, import_yup29.string)().optional().nullable().label("Release Tag"),
|
|
3026
|
+
/** The tag of the last successful run, i.e. what this one is replacing. */
|
|
3027
|
+
previousReleaseTag: (0, import_yup29.string)().optional().nullable().label("Previous Release Tag"),
|
|
3028
|
+
startedAt: (0, import_yup29.number)().required().label("Started At"),
|
|
3029
|
+
finishedAt: (0, import_yup29.number)().optional().nullable().label("Finished At"),
|
|
3030
|
+
durationMs: (0, import_yup29.number)().optional().nullable().label("Duration (ms)"),
|
|
3031
|
+
phase: (0, import_yup29.mixed)().oneOf(SupportedGeoSyncPhases).optional().nullable().label("Phase"),
|
|
3032
|
+
/**
|
|
3033
|
+
* Live progress for the dashboard bar.
|
|
3034
|
+
*
|
|
3035
|
+
* `total` is an ESTIMATE — the previous successful run's count for the same
|
|
3036
|
+
* collection, or a hardcoded fallback on the very first run, since the true
|
|
3037
|
+
* total is not known until the stream ends. The UI should render it as one.
|
|
3038
|
+
*/
|
|
3039
|
+
progress: (0, import_yup29.object)({
|
|
3040
|
+
collection: (0, import_yup29.string)().optional().nullable().label("Collection"),
|
|
3041
|
+
processed: (0, import_yup29.number)().integer().min(0).default(0).label("Processed"),
|
|
3042
|
+
total: (0, import_yup29.number)().integer().min(0).optional().nullable().label("Total")
|
|
3043
|
+
}).optional().nullable().label("Progress"),
|
|
3044
|
+
counts: (0, import_yup29.object)({
|
|
3045
|
+
countries: GeoSyncCountsSchema.optional().nullable().label("Countries"),
|
|
3046
|
+
states: GeoSyncCountsSchema.optional().nullable().label("States"),
|
|
3047
|
+
cities: GeoSyncCountsSchema.optional().nullable().label("Cities")
|
|
3048
|
+
}).optional().nullable().label("Counts"),
|
|
3049
|
+
/** Why a `skipped` run was skipped, in words the dashboard can show verbatim. */
|
|
3050
|
+
reason: (0, import_yup29.string)().optional().nullable().label("Reason"),
|
|
3051
|
+
/** The failure message on a `failed` run. Surfaced verbatim to the admin. */
|
|
3052
|
+
error: (0, import_yup29.string)().optional().nullable().label("Error")
|
|
3053
|
+
}).label("Geo Sync Run");
|
|
3054
|
+
|
|
3055
|
+
// src/language/language-filter.schema.ts
|
|
3056
|
+
var import_yup30 = require("yup");
|
|
3057
|
+
var LanguageFilterShape = (0, import_yup30.object)({
|
|
3058
|
+
/** Language names, matched exactly. Free-text matching is `?search=`. */
|
|
3059
|
+
names: tokens("Names")
|
|
3060
|
+
}).label("Language Filter");
|
|
3061
|
+
var LanguageFilterSchema = rejectUnknownKeys(
|
|
3062
|
+
LanguageFilterShape,
|
|
3063
|
+
() => Object.keys(LanguageFilterShape.fields)
|
|
3064
|
+
);
|
|
3065
|
+
|
|
3066
|
+
// src/page/page-filter.schema.ts
|
|
3067
|
+
var import_yup31 = require("yup");
|
|
3068
|
+
var PageFilterShape = (0, import_yup31.object)({
|
|
3069
|
+
/**
|
|
3070
|
+
* `type`, NOT `pageType`.
|
|
3071
|
+
*
|
|
3072
|
+
* The enum constant is named `PageType`, which makes `pageType` the obvious
|
|
3073
|
+
* guess, and it is wrong: the stored key is `type` (verified — `pageType`
|
|
3074
|
+
* exists on 0 of 1,449 documents, while `type` splits 1,198 company / 251
|
|
3075
|
+
* institute). The name of a constant is not the name of a field.
|
|
3076
|
+
*/
|
|
3077
|
+
type: enumFacet(SupportedPageTypes, "Page Type"),
|
|
3078
|
+
/**
|
|
3079
|
+
* Publication state. Worth knowing when filtering: 69 documents carry NO
|
|
3080
|
+
* `status` at all and some carry `null`, so `status: ['published']` is a real
|
|
3081
|
+
* narrowing (1,379 of 1,449) and absent means "no constraint", not "published".
|
|
3082
|
+
*/
|
|
3083
|
+
status: enumFacet(SupportedPageStatuses, "Status"),
|
|
3084
|
+
// ── Flags ───────────────────────────────────────────────────────────────────
|
|
3085
|
+
// No `.default(false)`: absent means no constraint. Note `isFeatured` is
|
|
3086
|
+
// present on only 55 documents, so combining it with `status: ['published']`
|
|
3087
|
+
// leaves 11 pages — a legitimate filter that returns almost nothing.
|
|
3088
|
+
isFeatured: (0, import_yup31.boolean)().optional().label("Featured Only"),
|
|
3089
|
+
/** Lowercase (`in`, `se`, `ww`), like groups. See `GroupFilterSchema`. */
|
|
3090
|
+
featuredMarkets: tokens("Featured Markets"),
|
|
3091
|
+
/** Page SLUGS. A company is matched by slug: a display name breaks on rename. */
|
|
3092
|
+
slugs: tokens("Slugs")
|
|
3093
|
+
}).label("Page Filter");
|
|
3094
|
+
var PAGE_FILTER_KEYS = Object.keys(PageFilterShape.fields);
|
|
3095
|
+
var PageFilterSchema = rejectUnknownKeys(
|
|
3096
|
+
PageFilterShape,
|
|
3097
|
+
() => PAGE_FILTER_KEYS
|
|
3098
|
+
);
|
|
3099
|
+
|
|
3100
|
+
// src/marketing/marketing.schema.ts
|
|
3101
|
+
var import_yup33 = require("yup");
|
|
2244
3102
|
|
|
2245
3103
|
// src/marketing/marketing.constant.ts
|
|
2246
3104
|
var CampaignStatus = /* @__PURE__ */ ((CampaignStatus2) => {
|
|
2247
3105
|
CampaignStatus2["Draft"] = "draft";
|
|
2248
|
-
CampaignStatus2["
|
|
2249
|
-
CampaignStatus2["
|
|
3106
|
+
CampaignStatus2["Running"] = "running";
|
|
3107
|
+
CampaignStatus2["Paused"] = "paused";
|
|
3108
|
+
CampaignStatus2["Stopped"] = "stopped";
|
|
3109
|
+
CampaignStatus2["Completed"] = "completed";
|
|
2250
3110
|
CampaignStatus2["Failed"] = "failed";
|
|
2251
3111
|
return CampaignStatus2;
|
|
2252
3112
|
})(CampaignStatus || {});
|
|
2253
3113
|
var SupportedCampaignStatuses = Object.values(CampaignStatus);
|
|
3114
|
+
var TerminalCampaignStatuses = [
|
|
3115
|
+
"stopped" /* Stopped */,
|
|
3116
|
+
"completed" /* Completed */,
|
|
3117
|
+
"failed" /* Failed */
|
|
3118
|
+
];
|
|
3119
|
+
var CampaignStatusTransitions = {
|
|
3120
|
+
["draft" /* Draft */]: ["running" /* Running */],
|
|
3121
|
+
["running" /* Running */]: [
|
|
3122
|
+
"paused" /* Paused */,
|
|
3123
|
+
"stopped" /* Stopped */,
|
|
3124
|
+
"completed" /* Completed */,
|
|
3125
|
+
"failed" /* Failed */
|
|
3126
|
+
],
|
|
3127
|
+
["paused" /* Paused */]: ["running" /* Running */, "stopped" /* Stopped */],
|
|
3128
|
+
["stopped" /* Stopped */]: [],
|
|
3129
|
+
["completed" /* Completed */]: [],
|
|
3130
|
+
["failed" /* Failed */]: []
|
|
3131
|
+
};
|
|
3132
|
+
function canTransitionCampaign(from, to) {
|
|
3133
|
+
return CampaignStatusTransitions[from]?.includes(to) ?? false;
|
|
3134
|
+
}
|
|
2254
3135
|
var CampaignTriggerKind = /* @__PURE__ */ ((CampaignTriggerKind2) => {
|
|
2255
3136
|
CampaignTriggerKind2["Manual"] = "manual";
|
|
2256
3137
|
CampaignTriggerKind2["Once"] = "once";
|
|
2257
3138
|
CampaignTriggerKind2["Schedule"] = "schedule";
|
|
2258
|
-
CampaignTriggerKind2["Lifecycle"] = "lifecycle";
|
|
2259
3139
|
return CampaignTriggerKind2;
|
|
2260
3140
|
})(CampaignTriggerKind || {});
|
|
2261
3141
|
var SupportedCampaignTriggerKinds = Object.values(CampaignTriggerKind);
|
|
@@ -2273,21 +3153,16 @@ var CampaignEndKind = /* @__PURE__ */ ((CampaignEndKind2) => {
|
|
|
2273
3153
|
return CampaignEndKind2;
|
|
2274
3154
|
})(CampaignEndKind || {});
|
|
2275
3155
|
var SupportedCampaignEndKinds = Object.values(CampaignEndKind);
|
|
2276
|
-
var
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
var
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
BindingKind2["Url"] = "url";
|
|
2287
|
-
BindingKind2["Source"] = "source";
|
|
2288
|
-
return BindingKind2;
|
|
2289
|
-
})(BindingKind || {});
|
|
2290
|
-
var SupportedBindingKinds = Object.values(BindingKind);
|
|
3156
|
+
var TemplateVariableBindingKind = /* @__PURE__ */ ((TemplateVariableBindingKind2) => {
|
|
3157
|
+
TemplateVariableBindingKind2["Static"] = "static";
|
|
3158
|
+
TemplateVariableBindingKind2["Recipient"] = "recipient";
|
|
3159
|
+
TemplateVariableBindingKind2["Url"] = "url";
|
|
3160
|
+
TemplateVariableBindingKind2["Source"] = "source";
|
|
3161
|
+
return TemplateVariableBindingKind2;
|
|
3162
|
+
})(TemplateVariableBindingKind || {});
|
|
3163
|
+
var SupportedTemplateVariableBindingKinds = Object.values(
|
|
3164
|
+
TemplateVariableBindingKind
|
|
3165
|
+
);
|
|
2291
3166
|
var OccurrenceStatus = /* @__PURE__ */ ((OccurrenceStatus2) => {
|
|
2292
3167
|
OccurrenceStatus2["Pending"] = "pending";
|
|
2293
3168
|
OccurrenceStatus2["Running"] = "running";
|
|
@@ -2299,24 +3174,50 @@ var OccurrenceStatus = /* @__PURE__ */ ((OccurrenceStatus2) => {
|
|
|
2299
3174
|
var SupportedOccurrenceStatuses = Object.values(OccurrenceStatus);
|
|
2300
3175
|
var CAMPAIGN_MAX_RECIPIENTS_CEILING = 5e3;
|
|
2301
3176
|
|
|
2302
|
-
// src/
|
|
2303
|
-
var
|
|
2304
|
-
var
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
3177
|
+
// src/user/user-filter.schema.ts
|
|
3178
|
+
var import_yup32 = require("yup");
|
|
3179
|
+
var UserFilterBranchShape = (0, import_yup32.object)({
|
|
3180
|
+
/**
|
|
3181
|
+
* Exact email addresses. An IDENTITY lookup, not a search.
|
|
3182
|
+
*
|
|
3183
|
+
* The admin `search` box already matches email, but as a case-insensitive
|
|
3184
|
+
* unanchored regex over email + first + last, which no index can seek: it
|
|
3185
|
+
* scans every key. Measured against 103,680 users, finding one person by
|
|
3186
|
+
* their full address examined all 103,680 documents and took 552ms, where the
|
|
3187
|
+
* same lookup as an exact match on the indexed `email` field is 0ms and
|
|
3188
|
+
* examines 1.
|
|
3189
|
+
*
|
|
3190
|
+
* So this is for when the caller ALREADY KNOWS the address (previewing what
|
|
3191
|
+
* one named recipient will receive, reconciling a bounce) and `search` is for
|
|
3192
|
+
* when a human is typing a fragment.
|
|
3193
|
+
*
|
|
3194
|
+
* Values are LOWER-CASED before matching, not matched case-insensitively: all
|
|
3195
|
+
* 103,680 stored addresses are already lower case (verified), so normalising
|
|
3196
|
+
* the input keeps the match an indexed equality instead of a regex that would
|
|
3197
|
+
* put the scan straight back.
|
|
3198
|
+
*/
|
|
3199
|
+
emails: (0, import_yup32.array)().of((0, import_yup32.string)().required()).optional().default([]).label("Email Addresses"),
|
|
3200
|
+
/**
|
|
3201
|
+
* Better Auth account ids. An IDENTITY lookup like `emails`, and for the same
|
|
3202
|
+
* reason: the caller already knows the exact value.
|
|
3203
|
+
*
|
|
3204
|
+
* BACKEND-ONLY, despite living beside the human-facing facets. The consumer
|
|
3205
|
+
* is hono-auth, which holds an account id and needs the profile's `shortId`
|
|
3206
|
+
* to sign into the JWT it is minting. It cannot use `GET /me` (it is the one
|
|
3207
|
+
* issuing the token) and cannot use `GET /users/:shortId` (the shortId is
|
|
3208
|
+
* precisely what it lacks), so the list route with a facet is the lookup.
|
|
3209
|
+
*
|
|
3210
|
+
* Matched as an indexed equality on `authAccountId`, which is already indexed
|
|
3211
|
+
* as the mirror's link key. No case normalisation, unlike `emails`: these are
|
|
3212
|
+
* opaque generated ids, stored exactly as Better Auth issued them, so folding
|
|
3213
|
+
* case could only ever turn a hit into a miss.
|
|
3214
|
+
*
|
|
3215
|
+
* The admin filter panel builds its controls explicitly rather than from this
|
|
3216
|
+
* shape, so declaring it here does not put an account-id box in the UI.
|
|
3217
|
+
*/
|
|
3218
|
+
authAccountIds: (0, import_yup32.array)().of((0, import_yup32.string)().required()).optional().default([]).label("Auth Account IDs"),
|
|
2318
3219
|
// ── Enum facets (closed vocabularies, chip pickers) ─────────────────────────
|
|
2319
|
-
countries: (0,
|
|
3220
|
+
countries: (0, import_yup32.array)().of((0, import_yup32.string)().required()).optional().default([]).label("Countries"),
|
|
2320
3221
|
roles: enumFacet(SupportedUserRoles, "Roles"),
|
|
2321
3222
|
// The user's own current seniority (UserSchema.experienceLevel).
|
|
2322
3223
|
experienceLevels: enumFacet(SupportedExperienceLevels, "Experience Levels"),
|
|
@@ -2332,40 +3233,112 @@ var ListFilterBranchSchema = (0, import_yup20.object)({
|
|
|
2332
3233
|
SupportedUserProfileVisibilities,
|
|
2333
3234
|
"Profile Visibility"
|
|
2334
3235
|
),
|
|
2335
|
-
// Proficiency held on any skill entry (skills[].proficiencyLevel).
|
|
2336
|
-
skillProficiencyLevels: enumFacet(
|
|
2337
|
-
SupportedProficiencyLevels,
|
|
2338
|
-
"Skill Proficiency"
|
|
2339
|
-
),
|
|
2340
|
-
// Proficiency held on any language entry (languages[].proficiencyLevel).
|
|
2341
|
-
languageProficiencyLevels: enumFacet(
|
|
2342
|
-
SupportedProficiencyLevels,
|
|
2343
|
-
"Language Proficiency"
|
|
2344
|
-
),
|
|
2345
3236
|
// Mode of any education entry (educations[].studyType).
|
|
3237
|
+
//
|
|
3238
|
+
// Left as a STANDALONE enum, unlike skill/language proficiency above: it is a
|
|
3239
|
+
// property of a person's education in general ("studied full time"), and the
|
|
3240
|
+
// measured divergence against pairing it with `courses` was 1 user out of
|
|
3241
|
+
// 3,465. Pairing is for qualifiers that are MEANINGLESS alone; this one is
|
|
3242
|
+
// not, so it stays cheap to use.
|
|
2346
3243
|
studyTypes: enumFacet(SupportedStudyTypes, "Study Type"),
|
|
2347
3244
|
// Platforms the user linked a social account for (socialAccounts[].type).
|
|
2348
3245
|
socialAccountTypes: enumFacet(SupportedSocialAccounts, "Social Accounts"),
|
|
2349
3246
|
// Currency of the user's salary floor (minSalaryCurrency).
|
|
2350
3247
|
salaryCurrencies: enumFacet(SupportedSalaryCurrencies, "Salary Currency"),
|
|
2351
3248
|
// ── Free-text token facets (nested profile arrays) ──────────────────────────
|
|
2352
|
-
|
|
2353
|
-
|
|
3249
|
+
// Skill and language each carry their OWN proficiency, because a level is
|
|
3250
|
+
// only meaningful attached to the thing it qualifies. See `qualified`:
|
|
3251
|
+
// as two independent facets, "python + expert" returned 301 users where only
|
|
3252
|
+
// 103 were actually expert at Python.
|
|
3253
|
+
//
|
|
3254
|
+
// `[{ name: 'python', proficiencyLevel: 'expert' }]` — the level is optional,
|
|
3255
|
+
// so naming a skill without one still filters on the skill alone.
|
|
3256
|
+
skills: qualified(SupportedProficiencyLevels, "proficiencyLevel", "Skills"),
|
|
3257
|
+
languages: qualified(SupportedProficiencyLevels, "proficiencyLevel", "Languages"),
|
|
2354
3258
|
interests: tokens("Interests"),
|
|
2355
|
-
|
|
3259
|
+
// LOOSE forms, kept deliberately. Each of these is a real question on its own
|
|
3260
|
+
// ("worked at Google", "was an engineer somewhere"), and asking for two of
|
|
3261
|
+
// them usually does mean "has each of these somewhere in their history".
|
|
3262
|
+
// They AND across the whole array, so different entries may satisfy each.
|
|
3263
|
+
//
|
|
3264
|
+
// `companyName` / `instituteName`, NOT the `company` widget the job filter
|
|
3265
|
+
// uses. Both are PICKED from real pages — a profile stores the whole
|
|
3266
|
+
// `PageSchema.pick(["name","slug","type","logo"])` object, not free text — but
|
|
3267
|
+
// the facet matches `workExperiences.company.name`, so the value to store is
|
|
3268
|
+
// the page's NAME. Storing its slug would be a picker that matches nothing.
|
|
3269
|
+
companies: tokens("Companies").meta({ widget: "companyName" }),
|
|
2356
3270
|
designations: tokens("Job Titles / Designations"),
|
|
2357
|
-
institutes: tokens("Institutes"),
|
|
3271
|
+
institutes: tokens("Institutes").meta({ widget: "instituteName" }),
|
|
2358
3272
|
courses: tokens("Courses"),
|
|
2359
3273
|
fieldsOfStudy: tokens("Fields of Study"),
|
|
2360
|
-
|
|
2361
|
-
|
|
3274
|
+
// PRECISE forms: all parts must describe ONE entry.
|
|
3275
|
+
//
|
|
3276
|
+
// The narrower question the loose facets above cannot express. "Engineer AT
|
|
3277
|
+
// Google" as `companies=Google & designations=engineer` also matches someone
|
|
3278
|
+
// who was an engineer at Acme and a designer at Google, because Mongo
|
|
3279
|
+
// satisfies each path from a different array entry. Measured on production:
|
|
3280
|
+
// VIT + B.Tech returned 71 users where 64 had one education entry that was
|
|
3281
|
+
// both, and Infosys + Test Engineer returned 7 where 6 actually matched.
|
|
3282
|
+
//
|
|
3283
|
+
// Additive rather than a replacement: both readings are legitimate, so the
|
|
3284
|
+
// vocabulary carries both and the caller says which it means. 1,573 users
|
|
3285
|
+
// have two or more work entries and 1,761 have two or more educations, so the
|
|
3286
|
+
// distinction is real and grows as profiles fill in.
|
|
3287
|
+
//
|
|
3288
|
+
// The fields mirror what the profile form actually collects for one entry
|
|
3289
|
+
// (`WorkExperienceSchema` / `EducationSchema`), minus the parts that cannot
|
|
3290
|
+
// narrow an audience: `description` is prose, and a per-entry `location`
|
|
3291
|
+
// duplicates the top-level geography facets against a much emptier path.
|
|
3292
|
+
//
|
|
3293
|
+
// `isCurrent` reads `duration.isActive`, which is what the profile form calls
|
|
3294
|
+
// "I currently work here". It is the field that makes this facet worth having
|
|
3295
|
+
// per entry: "engineer at Google" and "engineer at Google NOW" are different
|
|
3296
|
+
// audiences, and only the second is reachable without it. Live counts:
|
|
3297
|
+
// 2,190 users have a current job against 4,527 with a past one, 1,456 have a
|
|
3298
|
+
// remote entry, 4,088 are still studying.
|
|
3299
|
+
workHistory: conjunction(
|
|
3300
|
+
[
|
|
3301
|
+
"company",
|
|
3302
|
+
"designation",
|
|
3303
|
+
{ name: "isCurrent", type: "boolean" },
|
|
3304
|
+
{ name: "isRemote", type: "boolean" }
|
|
3305
|
+
],
|
|
3306
|
+
"Work History"
|
|
3307
|
+
),
|
|
3308
|
+
educationHistory: conjunction(
|
|
3309
|
+
[
|
|
3310
|
+
"institute",
|
|
3311
|
+
"course",
|
|
3312
|
+
"fieldOfStudy",
|
|
3313
|
+
{ name: "studyType", type: "string", oneOf: SupportedStudyTypes },
|
|
3314
|
+
{ name: "isCurrent", type: "boolean" },
|
|
3315
|
+
{ name: "isDistanceLearning", type: "boolean" }
|
|
3316
|
+
],
|
|
3317
|
+
"Education History"
|
|
3318
|
+
),
|
|
3319
|
+
// The issuing body belongs to the certificate, not to the person: "AWS SA"
|
|
3320
|
+
// plus "Microsoft" as two facets matches someone holding an AWS cert AND any
|
|
3321
|
+
// unrelated Microsoft one. 38% of users with a certification hold two or more,
|
|
3322
|
+
// so the entries are there to diverge.
|
|
3323
|
+
//
|
|
3324
|
+
// `authority` is free text rather than a closed vocabulary, hence `"open"`.
|
|
3325
|
+
certifications: qualified("open", "authority", "Certifications"),
|
|
2362
3326
|
projects: tokens("Projects"),
|
|
2363
3327
|
jobRoles: tokens("Target Roles"),
|
|
2364
3328
|
jobLocations: tokens("Preferred Job Locations"),
|
|
2365
3329
|
// Companies a recruiter is hiring for (recruiterProfile.hiringFor[].page.name).
|
|
2366
|
-
|
|
3330
|
+
// A picked page (`recruiterProfile.hiringFor.page`), matched on `.name`.
|
|
3331
|
+
//
|
|
3332
|
+
// `pageName`, which searches BOTH page types: `RecruiterPageLinkSchema` picks
|
|
3333
|
+
// `PageSchema` without constraining `type`, so a recruiter may be hiring for
|
|
3334
|
+
// an institute as legitimately as for a company. Production carries exactly
|
|
3335
|
+
// that on the coordinator side — a `coordinatesAt` entry holding a company —
|
|
3336
|
+
// so scoping either of these to one type would hide real data.
|
|
3337
|
+
hiringForCompanies: tokens("Hiring For").meta({ widget: "pageName" }),
|
|
2367
3338
|
// Institutes a coordinator works at (coordinatorProfile.coordinatesAt[].page.name).
|
|
2368
|
-
|
|
3339
|
+
// Same: a picked page (`coordinatorProfile.coordinatesAt.page`), on `.name`,
|
|
3340
|
+
// and likewise unconstrained in `type`.
|
|
3341
|
+
coordinatesAtInstitutes: tokens("Coordinates At").meta({ widget: "pageName" }),
|
|
2369
3342
|
// Case-insensitive keyword match against headline + aboutMe.
|
|
2370
3343
|
keywords: tokens("Profile Keywords"),
|
|
2371
3344
|
// ── Range / boolean facets ──────────────────────────────────────────────────
|
|
@@ -2373,14 +3346,16 @@ var ListFilterBranchSchema = (0, import_yup20.object)({
|
|
|
2373
3346
|
// integer and 50000 SEK is not 50000 USD, so an un-currencied range matches
|
|
2374
3347
|
// across currencies and quietly means nothing. Requiring exactly one currency
|
|
2375
3348
|
// alongside a bound is what makes the comparison sound.
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
3349
|
+
// The sibling currency facet is named explicitly: this bound is inert without
|
|
3350
|
+
// it, so the dependency is stated rather than assumed.
|
|
3351
|
+
minSalaryFloor: currencyBound("Min Salary \u2265", "salaryCurrencies"),
|
|
3352
|
+
minSalaryCeil: currencyBound("Min Salary \u2264", "salaryCurrencies"),
|
|
3353
|
+
openToWorkOnly: (0, import_yup32.boolean)().optional().default(false).label("Open to Work Only"),
|
|
3354
|
+
remoteExperienceOnly: (0, import_yup32.boolean)().optional().default(false).label("Has Remote Experience"),
|
|
3355
|
+
hasResumeOnly: (0, import_yup32.boolean)().optional().default(false).label("Has Resume"),
|
|
3356
|
+
onboardingCompletedOnly: (0, import_yup32.boolean)().optional().default(false).label("Onboarding Completed"),
|
|
3357
|
+
emailVerifiedOnly: (0, import_yup32.boolean)().optional().default(false).label("Email Verified Only"),
|
|
3358
|
+
mobileVerifiedOnly: (0, import_yup32.boolean)().optional().default(false).label("Mobile Verified Only"),
|
|
2384
3359
|
// ── Lifecycle windows ───────────────────────────────────────────────────────
|
|
2385
3360
|
// Relative, because a saved list is resolved fresh on every send: a stored
|
|
2386
3361
|
// absolute epoch would freeze at authoring time and the audience would decay
|
|
@@ -2408,10 +3383,10 @@ var ListFilterBranchSchema = (0, import_yup20.object)({
|
|
|
2408
3383
|
// Radius search around a point, against location.geo. All three required
|
|
2409
3384
|
// together; validated as a group rather than field-by-field so a half-filled
|
|
2410
3385
|
// radius cannot silently match everyone.
|
|
2411
|
-
withinRadius: (0,
|
|
2412
|
-
lat: (0,
|
|
2413
|
-
lng: (0,
|
|
2414
|
-
km: (0,
|
|
3386
|
+
withinRadius: (0, import_yup32.object)({
|
|
3387
|
+
lat: (0, import_yup32.number)().min(-90).max(90).required().label("Latitude"),
|
|
3388
|
+
lng: (0, import_yup32.number)().min(-180).max(180).required().label("Longitude"),
|
|
3389
|
+
km: (0, import_yup32.number)().moreThan(0).max(2e4).required().label("Radius (km)")
|
|
2415
3390
|
}).optional().default(void 0).label("Within Radius"),
|
|
2416
3391
|
// ── Signup context (immutable market / locale) ──────────────────────────────
|
|
2417
3392
|
signupCountries: tokens("Signup Countries"),
|
|
@@ -2422,14 +3397,27 @@ var ListFilterBranchSchema = (0, import_yup20.object)({
|
|
|
2422
3397
|
// match — filtering on this necessarily excludes them.
|
|
2423
3398
|
educationLevels: enumFacet(SupportedEducationLevels, "Education Level"),
|
|
2424
3399
|
// ── Consent metadata ────────────────────────────────────────────────────────
|
|
2425
|
-
// `
|
|
2426
|
-
// applied unconditionally by user-service
|
|
3400
|
+
// `marketingSubscribed` IS a facet, and deliberately so. It used to be a hard
|
|
3401
|
+
// opt-in gate applied unconditionally by user-service; that gate was removed
|
|
3402
|
+
// when user-service became a pure data source, so consent is now an ordinary
|
|
3403
|
+
// filter that marketing-service defaults to `true` in `resolveAudience`.
|
|
3404
|
+
//
|
|
3405
|
+
// NO `.default()` HERE, unlike every other boolean facet. The send spreads its
|
|
3406
|
+
// default first and lets a stored value win, so this must distinguish "the
|
|
3407
|
+
// list said nothing" (absent -> default applies) from "the list said false"
|
|
3408
|
+
// (override -> mails users who never opted in). A `.default(false)` would
|
|
3409
|
+
// materialise the second case on every list that never chose it.
|
|
3410
|
+
//
|
|
3411
|
+
// @todo CONSENT-FILTER-ENFORCEMENT — nothing enforces consent anywhere now.
|
|
3412
|
+
marketingSubscribed: (0, import_yup32.boolean)().optional().label("Marketing Subscribed"),
|
|
2427
3413
|
jobAlertFrequencies: enumFacet(
|
|
2428
3414
|
SupportedJobAlertFrequencies,
|
|
2429
3415
|
"Job Alert Frequency"
|
|
2430
3416
|
),
|
|
2431
3417
|
consentSources: tokens("Consent Source"),
|
|
2432
3418
|
consentUpdatedWithinDays: days("Consent Updated Within (days)"),
|
|
3419
|
+
/** The absolute bound `consentUpdatedWithinDays` resolves into (epoch ms). */
|
|
3420
|
+
consentUpdatedAfter: epoch("Consent Updated After"),
|
|
2433
3421
|
// ── Notification preferences ────────────────────────────────────────────────
|
|
2434
3422
|
// Read as opt-IN (`=== true`), matching marketingConsent and NOT the `!== false`
|
|
2435
3423
|
// reading used for transactional notification routing. A campaign is bulk mail,
|
|
@@ -2437,19 +3425,31 @@ var ListFilterBranchSchema = (0, import_yup20.object)({
|
|
|
2437
3425
|
// `productUpdates` defaults to false on the user document, so targeting it
|
|
2438
3426
|
// selects only people who deliberately turned it on.
|
|
2439
3427
|
notificationChannelsEnabled: enumFacet(
|
|
2440
|
-
|
|
3428
|
+
SupportedNotificationTypes,
|
|
2441
3429
|
"Notification Channels Enabled"
|
|
2442
3430
|
),
|
|
2443
3431
|
// ── Tenure / recency ────────────────────────────────────────────────────────
|
|
2444
3432
|
// Against workExperiences[].duration and educations[].duration ("YYYY-MM").
|
|
2445
|
-
currentlyEmployed: (0,
|
|
3433
|
+
currentlyEmployed: (0, import_yup32.boolean)().optional().default(false).label("Currently Employed"),
|
|
2446
3434
|
graduatedWithinMonths: months("Graduated Within (months)"),
|
|
2447
3435
|
skillUsedWithinMonths: months("Skill Used Within (months)"),
|
|
3436
|
+
// The ABSOLUTE bounds the three relative windows above resolve into
|
|
3437
|
+
// (`graduatedWithinMonths` -> `graduatedAfter`, and so on). Both forms are
|
|
3438
|
+
// accepted: a caller may send either, and user-service derives the bound when
|
|
3439
|
+
// only the window is given.
|
|
3440
|
+
//
|
|
3441
|
+
// A UI should offer the WINDOW, not these. A saved list is re-resolved on
|
|
3442
|
+
// every send, so a stored absolute date rots — "graduated after 2024-03" keeps
|
|
3443
|
+
// meaning that forever, while "within 18 months" keeps meaning what the admin
|
|
3444
|
+
// intended. They are declared because the vocabulary must be complete, not
|
|
3445
|
+
// because a list should store one.
|
|
3446
|
+
graduatedAfter: (0, import_yup32.string)().trim().optional().label("Graduated After"),
|
|
3447
|
+
skillUsedAfter: (0, import_yup32.string)().trim().optional().label("Skill Used After"),
|
|
2448
3448
|
// ── Profile richness ────────────────────────────────────────────────────────
|
|
2449
|
-
hasImage: (0,
|
|
2450
|
-
hasHeadline: (0,
|
|
2451
|
-
hasAboutMe: (0,
|
|
2452
|
-
hasMobile: (0,
|
|
3449
|
+
hasImage: (0, import_yup32.boolean)().optional().default(false).label("Has Photo"),
|
|
3450
|
+
hasHeadline: (0, import_yup32.boolean)().optional().default(false).label("Has Headline"),
|
|
3451
|
+
hasAboutMe: (0, import_yup32.boolean)().optional().default(false).label("Has About Me"),
|
|
3452
|
+
hasMobile: (0, import_yup32.boolean)().optional().default(false).label("Has Mobile Number"),
|
|
2453
3453
|
// Segment by WHICH profile section is missing (profileCompleteness.missing[]),
|
|
2454
3454
|
// rather than only by the overall percentage.
|
|
2455
3455
|
missingSections: tokens("Missing Profile Sections"),
|
|
@@ -2461,50 +3461,39 @@ var ListFilterBranchSchema = (0, import_yup20.object)({
|
|
|
2461
3461
|
// data lives in other services' collections. Tri-state: `false` is a real
|
|
2462
3462
|
// filter ("has NO job alert"), which is the audience an alert nudge wants, so
|
|
2463
3463
|
// these are `undefined` when unset rather than defaulting to false.
|
|
2464
|
-
hasJobAlert: (0,
|
|
2465
|
-
hasApplied: (0,
|
|
2466
|
-
appliedWithinDays: days("Applied Within (days)")
|
|
2467
|
-
//
|
|
2468
|
-
|
|
3464
|
+
hasJobAlert: (0, import_yup32.boolean)().optional().default(void 0).label("Has a Job Alert"),
|
|
3465
|
+
hasApplied: (0, import_yup32.boolean)().optional().default(void 0).label("Has Applied"),
|
|
3466
|
+
appliedWithinDays: days("Applied Within (days)")
|
|
3467
|
+
// `search` is NOT here. It is free text over name/email, not a facet: it names
|
|
3468
|
+
// no field on a user document, user-service treats it as a RESERVED PARAM
|
|
3469
|
+
// beside the filter rather than a filter key, and it is meaningless inside an
|
|
3470
|
+
// `or` branch (a union of "matches this text" with a facet clause). It travels
|
|
3471
|
+
// as a sibling of the filter — see `PreviewFilterSchema` in marketing-service —
|
|
3472
|
+
// so a saved audience cannot carry free text it would silently re-run later.
|
|
2469
3473
|
}).label("Audience Filter Branch");
|
|
2470
|
-
var
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
);
|
|
2477
|
-
return `${label} has unsupported keys: ${bad.join(", ")}`;
|
|
2478
|
-
},
|
|
2479
|
-
(value) => {
|
|
2480
|
-
if (value == null) return true;
|
|
2481
|
-
const known = new Set(allowed());
|
|
2482
|
-
return Object.keys(value).every(
|
|
2483
|
-
(k) => known.has(k)
|
|
2484
|
-
);
|
|
2485
|
-
}
|
|
2486
|
-
);
|
|
2487
|
-
var BRANCH_KEYS = Object.keys(ListFilterBranchSchema.fields);
|
|
2488
|
-
var ListFilterBranch = rejectUnknownKeys(
|
|
2489
|
-
ListFilterBranchSchema,
|
|
2490
|
-
() => BRANCH_KEYS
|
|
2491
|
-
);
|
|
2492
|
-
var ListFilterSchema = rejectUnknownKeys(
|
|
2493
|
-
ListFilterBranchSchema.shape({
|
|
2494
|
-
or: (0, import_yup20.array)().of(ListFilterBranch).optional().default([]).label("Or Any Of")
|
|
2495
|
-
}).label("Audience Filter"),
|
|
3474
|
+
var BRANCH_KEYS = Object.keys(UserFilterBranchShape.fields);
|
|
3475
|
+
var UserFilterBranch = rejectUnknownKeys(UserFilterBranchShape, () => BRANCH_KEYS);
|
|
3476
|
+
var UserFilterSchema = rejectUnknownKeys(
|
|
3477
|
+
UserFilterBranchShape.shape({
|
|
3478
|
+
or: (0, import_yup32.array)().of(UserFilterBranch).optional().default([]).label("Or Any Of")
|
|
3479
|
+
}).label("User Filter"),
|
|
2496
3480
|
() => [...BRANCH_KEYS, "or"]
|
|
2497
3481
|
);
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
3482
|
+
|
|
3483
|
+
// src/marketing/marketing.schema.ts
|
|
3484
|
+
var MailingListFilterSchema = UserFilterSchema;
|
|
3485
|
+
var MailingListSchema = (0, import_yup33.object)({
|
|
3486
|
+
name: (0, import_yup33.string)().required().min(1).max(120).label("Name"),
|
|
3487
|
+
description: (0, import_yup33.string)().optional().max(500).label("Description"),
|
|
3488
|
+
filter: MailingListFilterSchema.default(
|
|
3489
|
+
() => MailingListFilterSchema.getDefault()
|
|
3490
|
+
)
|
|
2502
3491
|
}).label("Mailing List");
|
|
2503
|
-
var CampaignTriggerSchema = (0,
|
|
2504
|
-
kind: (0,
|
|
3492
|
+
var CampaignTriggerSchema = (0, import_yup33.object)({
|
|
3493
|
+
kind: (0, import_yup33.string)().oneOf(SupportedCampaignTriggerKinds).required().default("manual" /* Manual */).label("Trigger"),
|
|
2505
3494
|
// IANA zone, e.g. "Asia/Kolkata". Local wall-clock times are interpreted in it,
|
|
2506
3495
|
// so occurrences land at the intended hour across DST rather than drifting.
|
|
2507
|
-
timezone: (0,
|
|
3496
|
+
timezone: (0, import_yup33.string)().optional().default("UTC").label("Timezone").when("kind", {
|
|
2508
3497
|
is: (k) => k !== "manual" /* Manual */,
|
|
2509
3498
|
then: (s) => s.required()
|
|
2510
3499
|
}),
|
|
@@ -2518,26 +3507,26 @@ var CampaignTriggerSchema = (0, import_yup20.object)({
|
|
|
2518
3507
|
is: "schedule" /* Schedule */,
|
|
2519
3508
|
then: (s) => s.required()
|
|
2520
3509
|
}),
|
|
2521
|
-
interval: (0,
|
|
2522
|
-
every: (0,
|
|
2523
|
-
unit: (0,
|
|
3510
|
+
interval: (0, import_yup33.object)({
|
|
3511
|
+
every: (0, import_yup33.number)().integer().min(1).max(365).required().label("Every"),
|
|
3512
|
+
unit: (0, import_yup33.string)().oneOf(SupportedCampaignIntervalUnits).required().label("Unit")
|
|
2524
3513
|
}).optional().default(void 0).label("Repeats").when("kind", {
|
|
2525
3514
|
is: "schedule" /* Schedule */,
|
|
2526
3515
|
then: (s) => s.required()
|
|
2527
3516
|
}),
|
|
2528
3517
|
/** Local wall-clock send time, "HH:mm" in `timezone`. */
|
|
2529
|
-
timeOfDay: (0,
|
|
3518
|
+
timeOfDay: (0, import_yup33.string)().matches(/^([01]\d|2[0-3]):[0-5]\d$/, "Use HH:mm").optional().default("09:00").label("Time of Day"),
|
|
2530
3519
|
/** For weekly intervals: ISO weekdays, 1 = Monday .. 7 = Sunday. */
|
|
2531
|
-
byWeekday: (0,
|
|
3520
|
+
byWeekday: (0, import_yup33.array)().of((0, import_yup33.number)().integer().min(1).max(7).required()).optional().default([]).label("On Days"),
|
|
2532
3521
|
/**
|
|
2533
3522
|
* For monthly intervals: 1-31, or -1 meaning "last day of the month". A month
|
|
2534
3523
|
* shorter than the chosen day is clamped to its last day, so 31 in February
|
|
2535
3524
|
* fires on the 28th/29th rather than being skipped.
|
|
2536
3525
|
*/
|
|
2537
|
-
byMonthday: (0,
|
|
2538
|
-
end: (0,
|
|
2539
|
-
kind: (0,
|
|
2540
|
-
occurrences: (0,
|
|
3526
|
+
byMonthday: (0, import_yup33.number)().integer().min(-1).max(31).notOneOf([0]).optional().default(void 0).label("Day of Month"),
|
|
3527
|
+
end: (0, import_yup33.object)({
|
|
3528
|
+
kind: (0, import_yup33.string)().oneOf(SupportedCampaignEndKinds).required().label("Ends"),
|
|
3529
|
+
occurrences: (0, import_yup33.number)().integer().min(1).max(1e4).optional().default(void 0).label("After N Sends").when("kind", {
|
|
2541
3530
|
is: "afterOccurrences" /* AfterOccurrences */,
|
|
2542
3531
|
then: (s) => s.required()
|
|
2543
3532
|
}),
|
|
@@ -2545,52 +3534,80 @@ var CampaignTriggerSchema = (0, import_yup20.object)({
|
|
|
2545
3534
|
is: "onDate" /* OnDate */,
|
|
2546
3535
|
then: (s) => s.required()
|
|
2547
3536
|
})
|
|
2548
|
-
}).optional().default(() => ({ kind: "never" /* Never */ })).label("End Condition")
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
then: (s) => s.required()
|
|
2553
|
-
}),
|
|
2554
|
-
/** Days after the anchor at which this user becomes eligible. */
|
|
2555
|
-
offsetDays: (0, import_yup20.number)().integer().min(0).max(3650).optional().default(void 0).label("Days After Anchor").when("kind", {
|
|
2556
|
-
is: "lifecycle" /* Lifecycle */,
|
|
2557
|
-
then: (s) => s.required()
|
|
2558
|
-
}),
|
|
3537
|
+
}).optional().default(() => ({ kind: "never" /* Never */ })).label("End Condition")
|
|
3538
|
+
}).label("Trigger");
|
|
3539
|
+
var CampaignDataSourceSchema = (0, import_yup33.object)({
|
|
3540
|
+
id: (0, import_yup33.string)().required().label("Data Source"),
|
|
2559
3541
|
/**
|
|
2560
|
-
*
|
|
2561
|
-
*
|
|
2562
|
-
*
|
|
2563
|
-
*
|
|
3542
|
+
* Derive this source's filter per recipient from their profile, instead of
|
|
3543
|
+
* using the admin-entered `params` for everyone.
|
|
3544
|
+
*
|
|
3545
|
+
* A second AXIS on a datasource, not a datasource of its own: a
|
|
3546
|
+
* personalisation filter is an ordinary filter for that source, the only
|
|
3547
|
+
* difference being who wrote it. Jobs previously shipped the two modes as
|
|
3548
|
+
* two registry entries (`recent-jobs` / `matched-jobs`), which let them drift
|
|
3549
|
+
* apart down to their output names.
|
|
3550
|
+
*
|
|
3551
|
+
* Optional, and absent reads as false, so every existing campaign stays
|
|
3552
|
+
* un-personalised. Whether a source can honour it at all is declared by the
|
|
3553
|
+
* source itself (`supportsPersonalisation`), which this package cannot see.
|
|
2564
3554
|
*/
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
3555
|
+
personalised: (0, import_yup33.boolean)().optional().label("Personalised"),
|
|
3556
|
+
/**
|
|
3557
|
+
* A free-form bag whose keys are defined by the chosen source's OWN
|
|
3558
|
+
* `paramsSchema`, which this package cannot see. Same `lazy` treatment as
|
|
3559
|
+
* `bindings` above, and for the same reason: a bare `object()` has no known
|
|
3560
|
+
* keys, so under `stripUnknown: true` every param is "unknown" and the bag
|
|
3561
|
+
* arrives as `{}` — a campaign configured to mail remote Python roles in
|
|
3562
|
+
* Bangalore silently saves as an unfiltered platform-wide digest.
|
|
3563
|
+
*
|
|
3564
|
+
* Values stay unvalidated here on purpose; the marketing service validates
|
|
3565
|
+
* them against the real source schema in `checkCampaignConfig`, which is the
|
|
3566
|
+
* only place that knows what a given source accepts.
|
|
3567
|
+
*/
|
|
3568
|
+
params: (0, import_yup33.lazy)((value) => {
|
|
3569
|
+
const keys = value && typeof value === "object" ? Object.keys(value) : [];
|
|
3570
|
+
return (0, import_yup33.object)(
|
|
3571
|
+
Object.fromEntries(keys.map((k) => [k, (0, import_yup33.mixed)().optional()]))
|
|
3572
|
+
).optional().default(() => ({})).label("Parameters");
|
|
3573
|
+
})
|
|
3574
|
+
}).label("Data Source");
|
|
3575
|
+
var TemplateVariableBindingSchema = (0, import_yup33.object)({
|
|
3576
|
+
kind: (0, import_yup33.string)().oneOf(SupportedTemplateVariableBindingKinds).required().label("Source"),
|
|
3577
|
+
value: (0, import_yup33.string)().required().min(1).max(2e3).label("Value")
|
|
3578
|
+
}).label("Template Variable Binding");
|
|
3579
|
+
var CampaignSchema = (0, import_yup33.object)({
|
|
3580
|
+
name: (0, import_yup33.string)().required().min(1).max(200).label("Campaign Name"),
|
|
2573
3581
|
// The template renders its own subject; an override is only needed for the
|
|
2574
3582
|
// inline-HTML path, hence not required when a template is chosen.
|
|
2575
|
-
subject: (0,
|
|
3583
|
+
subject: (0, import_yup33.string)().optional().max(255).label("Subject").when("templateKey", {
|
|
2576
3584
|
is: (v) => !v,
|
|
2577
3585
|
then: (s) => s.required().min(1)
|
|
2578
3586
|
}),
|
|
2579
3587
|
/**
|
|
2580
3588
|
* Inline HTML body; may contain {{unsubscribe_url}}. Optional now that a
|
|
2581
3589
|
* campaign can instead name a registry template — exactly one of the two is
|
|
2582
|
-
* required. Scheduled
|
|
2583
|
-
*
|
|
3590
|
+
* required. Scheduled campaigns must use a template: the registry path is the
|
|
3591
|
+
* one with a compliance-checked footer and a render test,
|
|
2584
3592
|
* and hand-pasted HTML that repeats forever is a mistake that repeats forever.
|
|
2585
3593
|
*/
|
|
2586
|
-
html: (0,
|
|
3594
|
+
html: (0, import_yup33.string)().optional().label("HTML Body").when("templateKey", {
|
|
2587
3595
|
is: (v) => !v,
|
|
2588
3596
|
then: (s) => s.required().min(1)
|
|
2589
3597
|
}),
|
|
2590
|
-
text: (0,
|
|
2591
|
-
listId: (0,
|
|
3598
|
+
text: (0, import_yup33.string)().optional().label("Plain-text Body"),
|
|
3599
|
+
listId: (0, import_yup33.string)().required().label("Mailing List"),
|
|
3600
|
+
/**
|
|
3601
|
+
* Where the campaign is in its life. See `CampaignStatus` for the transitions.
|
|
3602
|
+
*
|
|
3603
|
+
* Declared here so the enum is ENFORCED on write. It was previously written
|
|
3604
|
+
* straight onto the document by the service with no schema behind it, and a
|
|
3605
|
+
* value the enum never contained (`scheduled`) reached production rows and
|
|
3606
|
+
* rendered in the admin list as though it were real.
|
|
3607
|
+
*/
|
|
3608
|
+
status: (0, import_yup33.string)().oneOf(SupportedCampaignStatuses).optional().default("draft" /* Draft */).label("Status"),
|
|
2592
3609
|
/** Key from email-service's template registry. Mutually exclusive with `html`. */
|
|
2593
|
-
templateKey: (0,
|
|
3610
|
+
templateKey: (0, import_yup33.string)().optional().label("Email Template"),
|
|
2594
3611
|
/**
|
|
2595
3612
|
* One entry per variable the chosen template declares. Validated at save time
|
|
2596
3613
|
* against the template's own `variables`, so a missing required binding is a
|
|
@@ -2605,58 +3622,52 @@ var CampaignSchema = (0, import_yup20.object)({
|
|
|
2605
3622
|
* whose required variables all appear unbound. Shaping each entry also means a
|
|
2606
3623
|
* malformed binding is rejected here rather than at send time.
|
|
2607
3624
|
*/
|
|
2608
|
-
bindings: (0,
|
|
3625
|
+
bindings: (0, import_yup33.lazy)((value) => {
|
|
2609
3626
|
const keys = value && typeof value === "object" ? Object.keys(value) : [];
|
|
2610
|
-
return (0,
|
|
2611
|
-
Object.fromEntries(keys.map((k) => [k,
|
|
3627
|
+
return (0, import_yup33.object)(
|
|
3628
|
+
Object.fromEntries(keys.map((k) => [k, TemplateVariableBindingSchema.required()]))
|
|
2612
3629
|
).optional().default(() => ({})).label("Template Data");
|
|
2613
3630
|
}),
|
|
2614
|
-
/**
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
* Bangalore silently saves as an unfiltered platform-wide digest.
|
|
2624
|
-
*
|
|
2625
|
-
* Values stay unvalidated here on purpose; the marketing service validates
|
|
2626
|
-
* them against the real source schema in `checkCampaignConfig`, which is the
|
|
2627
|
-
* only place that knows what a given source accepts.
|
|
2628
|
-
*/
|
|
2629
|
-
params: (0, import_yup20.lazy)((value) => {
|
|
2630
|
-
const keys = value && typeof value === "object" ? Object.keys(value) : [];
|
|
2631
|
-
return (0, import_yup20.object)(
|
|
2632
|
-
Object.fromEntries(keys.map((k) => [k, (0, import_yup20.mixed)().optional()]))
|
|
2633
|
-
).optional().default(() => ({})).label("Parameters");
|
|
2634
|
-
})
|
|
2635
|
-
}).optional().default(void 0).label("Data Source"),
|
|
3631
|
+
/**
|
|
3632
|
+
* The data sources this campaign draws content from.
|
|
3633
|
+
*
|
|
3634
|
+
* SEVERAL, because one mail may combine them. Each source's outputs are
|
|
3635
|
+
* NAMESPACED by its id in the rendered payload (`jobs.jobCount`,
|
|
3636
|
+
* `groups.groupCount`), so two sources providing the same name cannot
|
|
3637
|
+
* overwrite each other and a template says which one it means.
|
|
3638
|
+
*/
|
|
3639
|
+
dataSources: (0, import_yup33.array)().of(CampaignDataSourceSchema).optional().default(void 0).label("Data Sources"),
|
|
2636
3640
|
trigger: CampaignTriggerSchema.default(
|
|
2637
3641
|
() => CampaignTriggerSchema.getDefault()
|
|
2638
3642
|
),
|
|
2639
3643
|
// ── Safety ──────────────────────────────────────────────────────────────────
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
3644
|
+
/**
|
|
3645
|
+
* Still TWO deliberate acts before mail leaves, as before: a campaign must be
|
|
3646
|
+
* activated (`status: running`) AND taken out of dry run.
|
|
3647
|
+
*
|
|
3648
|
+
* The first half used to be a separate `enabled` boolean living alongside the
|
|
3649
|
+
* status, which meant two fields encoding one idea and able to disagree — an
|
|
3650
|
+
* `enabled: false` campaign whose status said it was sending was
|
|
3651
|
+
* representable, and the admin list showed exactly that. Activation is a
|
|
3652
|
+
* state transition, so it belongs in the state.
|
|
3653
|
+
*/
|
|
3654
|
+
dryRun: (0, import_yup33.boolean)().optional().default(true).label("Dry Run"),
|
|
3655
|
+
maxRecipients: (0, import_yup33.number)().integer().min(1).max(CAMPAIGN_MAX_RECIPIENTS_CEILING).optional().default(void 0).label("Max Recipients"),
|
|
3656
|
+
utm: (0, import_yup33.object)({
|
|
3657
|
+
campaign: (0, import_yup33.string)().optional().max(120).label("utm_campaign"),
|
|
3658
|
+
content: (0, import_yup33.string)().optional().max(120).label("utm_content")
|
|
2648
3659
|
}).optional().default(void 0).label("Link Tagging")
|
|
2649
3660
|
}).label("Campaign");
|
|
2650
3661
|
|
|
2651
3662
|
// src/pagination/pagination.schema.ts
|
|
2652
|
-
var
|
|
2653
|
-
var PaginationSchema = (0,
|
|
2654
|
-
page: (0,
|
|
2655
|
-
limit: (0,
|
|
2656
|
-
sortBy: (0,
|
|
2657
|
-
nextPage: (0,
|
|
2658
|
-
prevPage: (0,
|
|
2659
|
-
sortDirection: (0,
|
|
3663
|
+
var import_yup34 = require("yup");
|
|
3664
|
+
var PaginationSchema = (0, import_yup34.object)().shape({
|
|
3665
|
+
page: (0, import_yup34.number)().required().min(1).label("Page").default(1),
|
|
3666
|
+
limit: (0, import_yup34.number)().required().min(1).max(100).label("Limit").default(10),
|
|
3667
|
+
sortBy: (0, import_yup34.string)().notOneOf([""]).optional().label("SortBy"),
|
|
3668
|
+
nextPage: (0, import_yup34.string)().optional().label("Next Page Token"),
|
|
3669
|
+
prevPage: (0, import_yup34.string)().optional().label("Previous Page Token"),
|
|
3670
|
+
sortDirection: (0, import_yup34.string)().oneOf(["asc", "desc"]).optional().label("Sort Direction")
|
|
2660
3671
|
});
|
|
2661
3672
|
|
|
2662
3673
|
// src/pagination/pagination.constant.ts
|
|
@@ -2672,7 +3683,7 @@ var DefaultPaginationOptions = {
|
|
|
2672
3683
|
};
|
|
2673
3684
|
|
|
2674
3685
|
// src/saved-search/saved-search.schema.ts
|
|
2675
|
-
var
|
|
3686
|
+
var import_yup35 = require("yup");
|
|
2676
3687
|
|
|
2677
3688
|
// src/saved-search/saved-search.constant.ts
|
|
2678
3689
|
var SavedSearchStatus = /* @__PURE__ */ ((SavedSearchStatus2) => {
|
|
@@ -2683,21 +3694,21 @@ var SavedSearchStatus = /* @__PURE__ */ ((SavedSearchStatus2) => {
|
|
|
2683
3694
|
var SupportedSavedSearchStatuses = Object.values(SavedSearchStatus);
|
|
2684
3695
|
|
|
2685
3696
|
// src/saved-search/saved-search.schema.ts
|
|
2686
|
-
var SavedSearchSchema = (0,
|
|
3697
|
+
var SavedSearchSchema = (0, import_yup35.object)({
|
|
2687
3698
|
/** Public identifier. Server-assigned. */
|
|
2688
|
-
shortId: (0,
|
|
3699
|
+
shortId: (0, import_yup35.string)().trim().label("Short Id"),
|
|
2689
3700
|
/**
|
|
2690
3701
|
* Owner's Better Auth account id. Server-assigned from the verified token,
|
|
2691
3702
|
* never accepted from a request body.
|
|
2692
3703
|
*/
|
|
2693
|
-
ownerUserId: (0,
|
|
3704
|
+
ownerUserId: (0, import_yup35.string)().trim().label("Owner"),
|
|
2694
3705
|
/** What the user calls this search, e.g. "Remote React roles". */
|
|
2695
|
-
label: (0,
|
|
3706
|
+
label: (0, import_yup35.string)().trim().max(120).required().label("Label"),
|
|
2696
3707
|
/**
|
|
2697
3708
|
* The free-text query. Optional because a search can be filters-only ("all
|
|
2698
3709
|
* remote jobs in Sweden"), which is a legitimate alert to want.
|
|
2699
3710
|
*/
|
|
2700
|
-
search: (0,
|
|
3711
|
+
search: (0, import_yup35.string)().trim().max(500).optional().label("Search Text"),
|
|
2701
3712
|
/**
|
|
2702
3713
|
* Field filters, in the same `{ field: values }` shape the search endpoint
|
|
2703
3714
|
* parses from `filter[field]=a,b`. Held as a free-form object rather than a
|
|
@@ -2705,7 +3716,7 @@ var SavedSearchSchema = (0, import_yup22.object)({
|
|
|
2705
3716
|
* change independently; validating them here would put this package in the
|
|
2706
3717
|
* business of tracking that vocabulary.
|
|
2707
3718
|
*/
|
|
2708
|
-
filter: (0,
|
|
3719
|
+
filter: (0, import_yup35.object)().optional().default({}).label("Filters"),
|
|
2709
3720
|
/**
|
|
2710
3721
|
* How often to mail matches. `off` keeps the search saved but silent, which is
|
|
2711
3722
|
* the difference between "I don't want mail" and "delete my search".
|
|
@@ -2714,26 +3725,26 @@ var SavedSearchSchema = (0, import_yup22.object)({
|
|
|
2714
3725
|
* than defining a second one: two vocabularies for the same concept is how
|
|
2715
3726
|
* they drift.
|
|
2716
3727
|
*/
|
|
2717
|
-
frequency: (0,
|
|
2718
|
-
status: (0,
|
|
3728
|
+
frequency: (0, import_yup35.string)().oneOf(SupportedJobAlertFrequencies).default("off" /* Off */).label("Alert Frequency"),
|
|
3729
|
+
status: (0, import_yup35.string)().oneOf(SupportedSavedSearchStatuses).default("active" /* Active */).label("Status"),
|
|
2719
3730
|
/**
|
|
2720
3731
|
* Epoch ms of the last alert sent for this search. The matching sweep reads it
|
|
2721
3732
|
* to decide whether the cadence is due, and writes it only AFTER a successful
|
|
2722
3733
|
* publish, so a failed run retries next tick rather than silently skipping a
|
|
2723
3734
|
* period.
|
|
2724
3735
|
*/
|
|
2725
|
-
lastNotifiedAt: (0,
|
|
3736
|
+
lastNotifiedAt: (0, import_yup35.number)().optional().label("Last Notified At"),
|
|
2726
3737
|
/**
|
|
2727
3738
|
* Epoch ms high-water mark: only jobs created after this are "new" for the
|
|
2728
3739
|
* next alert. Kept separate from `lastNotifiedAt` because a run that finds no
|
|
2729
3740
|
* matches should still advance what counts as new, without implying mail was
|
|
2730
3741
|
* sent.
|
|
2731
3742
|
*/
|
|
2732
|
-
lastMatchedAt: (0,
|
|
3743
|
+
lastMatchedAt: (0, import_yup35.number)().optional().label("Last Matched At")
|
|
2733
3744
|
}).noUnknown().strict();
|
|
2734
3745
|
|
|
2735
3746
|
// src/report/report.schema.ts
|
|
2736
|
-
var
|
|
3747
|
+
var import_yup36 = require("yup");
|
|
2737
3748
|
|
|
2738
3749
|
// src/report/report.constant.ts
|
|
2739
3750
|
var ResourceType = /* @__PURE__ */ ((ResourceType3) => {
|
|
@@ -2757,15 +3768,15 @@ var ReportReason = /* @__PURE__ */ ((ReportReason3) => {
|
|
|
2757
3768
|
var SupportedReportReasons = Object.values(ReportReason);
|
|
2758
3769
|
|
|
2759
3770
|
// src/report/report.schema.ts
|
|
2760
|
-
var ReportSchema = (0,
|
|
2761
|
-
type: (0,
|
|
2762
|
-
resourceId: (0,
|
|
2763
|
-
reason: (0,
|
|
2764
|
-
comment: (0,
|
|
3771
|
+
var ReportSchema = (0, import_yup36.object)({
|
|
3772
|
+
type: (0, import_yup36.mixed)().oneOf(SupportedResourceTypes).required().label("Type"),
|
|
3773
|
+
resourceId: (0, import_yup36.string)().required().label("Resource ID"),
|
|
3774
|
+
reason: (0, import_yup36.mixed)().oneOf(SupportedReportReasons).required("Please choose a reason").label("Reason"),
|
|
3775
|
+
comment: (0, import_yup36.string)().max(1e3).optional().label("Comment")
|
|
2765
3776
|
}).label("Report Schema");
|
|
2766
3777
|
|
|
2767
|
-
// src/feedback/feedback.schema.ts
|
|
2768
|
-
var
|
|
3778
|
+
// src/feedback/feedback-filter.schema.ts
|
|
3779
|
+
var import_yup37 = require("yup");
|
|
2769
3780
|
|
|
2770
3781
|
// src/feedback/feedback.constant.ts
|
|
2771
3782
|
var FeedbackType = /* @__PURE__ */ ((FeedbackType3) => {
|
|
@@ -2787,64 +3798,317 @@ var FeedbackStatus = /* @__PURE__ */ ((FeedbackStatus3) => {
|
|
|
2787
3798
|
var SupportedFeedbackStatuses = Object.values(FeedbackStatus);
|
|
2788
3799
|
var FEEDBACK_MESSAGE_MAX = 4e3;
|
|
2789
3800
|
|
|
3801
|
+
// src/feedback/feedback-filter.schema.ts
|
|
3802
|
+
var FeedbackFilterShape = (0, import_yup37.object)({
|
|
3803
|
+
type: enumFacet(SupportedFeedbackTypes, "Type"),
|
|
3804
|
+
status: enumFacet(SupportedFeedbackStatuses, "Status")
|
|
3805
|
+
}).label("Feedback Filter");
|
|
3806
|
+
var FeedbackFilterSchema = rejectUnknownKeys(
|
|
3807
|
+
FeedbackFilterShape,
|
|
3808
|
+
() => Object.keys(FeedbackFilterShape.fields)
|
|
3809
|
+
);
|
|
3810
|
+
|
|
2790
3811
|
// src/feedback/feedback.schema.ts
|
|
2791
|
-
var
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
3812
|
+
var import_yup38 = require("yup");
|
|
3813
|
+
var FeedbackSchema = (0, import_yup38.object)({
|
|
3814
|
+
type: (0, import_yup38.mixed)().oneOf(SupportedFeedbackTypes).required().label("Type"),
|
|
3815
|
+
message: (0, import_yup38.string)().trim().required("Please enter a message").max(FEEDBACK_MESSAGE_MAX).label("Message"),
|
|
3816
|
+
email: (0, import_yup38.string)().email().optional().label("Email"),
|
|
3817
|
+
pageUrl: (0, import_yup38.string)().optional().label("Page URL"),
|
|
3818
|
+
status: (0, import_yup38.mixed)().oneOf(SupportedFeedbackStatuses).optional().label("Status")
|
|
2797
3819
|
}).label("Feedback Schema");
|
|
2798
3820
|
|
|
3821
|
+
// src/skill/skill-filter.schema.ts
|
|
3822
|
+
var import_yup39 = require("yup");
|
|
3823
|
+
var SkillFilterShape = (0, import_yup39.object)({
|
|
3824
|
+
/** Display names, matched exactly. Free-text matching is `?search=`. */
|
|
3825
|
+
names: tokens("Names"),
|
|
3826
|
+
/**
|
|
3827
|
+
* Taxonomy concept ids, e.g. "power-bi".
|
|
3828
|
+
*
|
|
3829
|
+
* `tokens`, NOT `codeTokens`: a conceptId is a lowercase slug, and
|
|
3830
|
+
* uppercasing it would match nothing.
|
|
3831
|
+
*/
|
|
3832
|
+
conceptIds: tokens("Concept IDs"),
|
|
3833
|
+
tags: tokens("Tags")
|
|
3834
|
+
}).label("Skill Filter");
|
|
3835
|
+
var SkillFilterSchema = rejectUnknownKeys(
|
|
3836
|
+
SkillFilterShape,
|
|
3837
|
+
() => Object.keys(SkillFilterShape.fields)
|
|
3838
|
+
);
|
|
3839
|
+
|
|
3840
|
+
// src/billing/billing.schema.ts
|
|
3841
|
+
var import_yup40 = require("yup");
|
|
3842
|
+
|
|
3843
|
+
// src/billing/billing.constant.ts
|
|
3844
|
+
var PlanKind = /* @__PURE__ */ ((PlanKind2) => {
|
|
3845
|
+
PlanKind2["Subscription"] = "subscription";
|
|
3846
|
+
PlanKind2["OneOff"] = "one-off";
|
|
3847
|
+
return PlanKind2;
|
|
3848
|
+
})(PlanKind || {});
|
|
3849
|
+
var SupportedPlanKinds = Object.values(PlanKind);
|
|
3850
|
+
var PlanStatus = /* @__PURE__ */ ((PlanStatus2) => {
|
|
3851
|
+
PlanStatus2["Draft"] = "draft";
|
|
3852
|
+
PlanStatus2["Active"] = "active";
|
|
3853
|
+
PlanStatus2["Archived"] = "archived";
|
|
3854
|
+
return PlanStatus2;
|
|
3855
|
+
})(PlanStatus || {});
|
|
3856
|
+
var SupportedPlanStatuses = Object.values(PlanStatus);
|
|
3857
|
+
var GrantType = /* @__PURE__ */ ((GrantType2) => {
|
|
3858
|
+
GrantType2["Boolean"] = "boolean";
|
|
3859
|
+
GrantType2["Quota"] = "quota";
|
|
3860
|
+
GrantType2["Credit"] = "credit";
|
|
3861
|
+
return GrantType2;
|
|
3862
|
+
})(GrantType || {});
|
|
3863
|
+
var SupportedGrantTypes = Object.values(GrantType);
|
|
3864
|
+
var QuotaPeriod = /* @__PURE__ */ ((QuotaPeriod2) => {
|
|
3865
|
+
QuotaPeriod2["Month"] = "month";
|
|
3866
|
+
QuotaPeriod2["Year"] = "year";
|
|
3867
|
+
QuotaPeriod2["Lifetime"] = "lifetime";
|
|
3868
|
+
return QuotaPeriod2;
|
|
3869
|
+
})(QuotaPeriod || {});
|
|
3870
|
+
var SupportedQuotaPeriods = Object.values(QuotaPeriod);
|
|
3871
|
+
var BillingPeriod = /* @__PURE__ */ ((BillingPeriod2) => {
|
|
3872
|
+
BillingPeriod2["Month"] = "month";
|
|
3873
|
+
BillingPeriod2["Year"] = "year";
|
|
3874
|
+
return BillingPeriod2;
|
|
3875
|
+
})(BillingPeriod || {});
|
|
3876
|
+
var SupportedBillingPeriods = Object.values(BillingPeriod);
|
|
3877
|
+
var SubscriptionStatus = /* @__PURE__ */ ((SubscriptionStatus2) => {
|
|
3878
|
+
SubscriptionStatus2["Active"] = "active";
|
|
3879
|
+
SubscriptionStatus2["Trialing"] = "trialing";
|
|
3880
|
+
SubscriptionStatus2["PastDue"] = "past_due";
|
|
3881
|
+
SubscriptionStatus2["Paused"] = "paused";
|
|
3882
|
+
SubscriptionStatus2["Canceled"] = "canceled";
|
|
3883
|
+
return SubscriptionStatus2;
|
|
3884
|
+
})(SubscriptionStatus || {});
|
|
3885
|
+
var SupportedSubscriptionStatuses = Object.values(SubscriptionStatus);
|
|
3886
|
+
var EntitlingSubscriptionStatuses = [
|
|
3887
|
+
"active" /* Active */,
|
|
3888
|
+
"trialing" /* Trialing */,
|
|
3889
|
+
"past_due" /* PastDue */
|
|
3890
|
+
];
|
|
3891
|
+
var TransactionStatus = /* @__PURE__ */ ((TransactionStatus2) => {
|
|
3892
|
+
TransactionStatus2["Draft"] = "draft";
|
|
3893
|
+
TransactionStatus2["Ready"] = "ready";
|
|
3894
|
+
TransactionStatus2["Billed"] = "billed";
|
|
3895
|
+
TransactionStatus2["Paid"] = "paid";
|
|
3896
|
+
TransactionStatus2["Completed"] = "completed";
|
|
3897
|
+
TransactionStatus2["Canceled"] = "canceled";
|
|
3898
|
+
TransactionStatus2["PastDue"] = "past_due";
|
|
3899
|
+
return TransactionStatus2;
|
|
3900
|
+
})(TransactionStatus || {});
|
|
3901
|
+
var SupportedTransactionStatuses = Object.values(TransactionStatus);
|
|
3902
|
+
var PaddleEnvironment = /* @__PURE__ */ ((PaddleEnvironment2) => {
|
|
3903
|
+
PaddleEnvironment2["Sandbox"] = "sandbox";
|
|
3904
|
+
PaddleEnvironment2["Production"] = "production";
|
|
3905
|
+
return PaddleEnvironment2;
|
|
3906
|
+
})(PaddleEnvironment || {});
|
|
3907
|
+
var SupportedPaddleEnvironments = Object.values(PaddleEnvironment);
|
|
3908
|
+
var FeatureKey = /* @__PURE__ */ ((FeatureKey2) => {
|
|
3909
|
+
FeatureKey2["JobPost"] = "job.post";
|
|
3910
|
+
FeatureKey2["JobAlerts"] = "job.alerts";
|
|
3911
|
+
FeatureKey2["PageCreate"] = "page.create";
|
|
3912
|
+
FeatureKey2["GroupCreate"] = "group.create";
|
|
3913
|
+
FeatureKey2["CandidateFinder"] = "candidate.finder";
|
|
3914
|
+
FeatureKey2["SearchAdvanced"] = "search.advanced";
|
|
3915
|
+
FeatureKey2["GetDiscovered"] = "get-discovered";
|
|
3916
|
+
return FeatureKey2;
|
|
3917
|
+
})(FeatureKey || {});
|
|
3918
|
+
var SupportedFeatureKeys = Object.values(FeatureKey);
|
|
3919
|
+
var EntitlementDenialReason = /* @__PURE__ */ ((EntitlementDenialReason2) => {
|
|
3920
|
+
EntitlementDenialReason2["NotEntitled"] = "not-entitled";
|
|
3921
|
+
EntitlementDenialReason2["QuotaExhausted"] = "quota-exhausted";
|
|
3922
|
+
EntitlementDenialReason2["NoCredits"] = "no-credits";
|
|
3923
|
+
return EntitlementDenialReason2;
|
|
3924
|
+
})(EntitlementDenialReason || {});
|
|
3925
|
+
var SupportedEntitlementDenialReasons = Object.values(
|
|
3926
|
+
EntitlementDenialReason
|
|
3927
|
+
);
|
|
3928
|
+
var GRANT_MAX_LIMIT = 1e5;
|
|
3929
|
+
var CONSUME_MAX_UNITS = 1e3;
|
|
3930
|
+
|
|
3931
|
+
// src/billing/billing.schema.ts
|
|
3932
|
+
var MoneySchema = (0, import_yup40.object)({
|
|
3933
|
+
amountMinor: (0, import_yup40.number)().integer().min(0).required().label("Amount (minor units)"),
|
|
3934
|
+
/**
|
|
3935
|
+
* ISO-4217, as returned by Paddle. Deliberately NOT constrained to
|
|
3936
|
+
* `SupportedSalaryCurrencies`: that list is the job-salary vocabulary, and
|
|
3937
|
+
* Paddle bills in currencies it does not contain. Constraining it here would
|
|
3938
|
+
* reject real payments.
|
|
3939
|
+
*/
|
|
3940
|
+
currencyCode: (0, import_yup40.string)().trim().uppercase().required().label("Currency")
|
|
3941
|
+
}).label("Money");
|
|
3942
|
+
var PlanGrantSchema = (0, import_yup40.object)({
|
|
3943
|
+
/**
|
|
3944
|
+
* The gated capability, e.g. `job.post`. A free string on purpose: see the
|
|
3945
|
+
* note on `FeatureKey` in `billing.constant.ts`. Adding a feature to the
|
|
3946
|
+
* paywall must not require releasing this package.
|
|
3947
|
+
*/
|
|
3948
|
+
feature: (0, import_yup40.string)().trim().required().min(1).max(64).label("Feature"),
|
|
3949
|
+
type: (0, import_yup40.string)().oneOf(SupportedGrantTypes).required().default("boolean" /* Boolean */).label("Grant Type"),
|
|
3950
|
+
/** Uses per period. Required for `quota`, meaningless otherwise. */
|
|
3951
|
+
limit: (0, import_yup40.number)().integer().min(1).max(GRANT_MAX_LIMIT).optional().label("Limit").when("type", {
|
|
3952
|
+
is: "quota" /* Quota */,
|
|
3953
|
+
then: (s) => s.required("A quota grant needs a limit")
|
|
3954
|
+
}),
|
|
3955
|
+
period: (0, import_yup40.string)().oneOf(SupportedQuotaPeriods).optional().default("month" /* Month */).label("Period").when("type", {
|
|
3956
|
+
is: "quota" /* Quota */,
|
|
3957
|
+
then: (s) => s.required("A quota grant needs a period")
|
|
3958
|
+
}),
|
|
3959
|
+
/** Non-expiring uses granted per purchase. Required for `credit`. */
|
|
3960
|
+
amount: (0, import_yup40.number)().integer().min(1).max(GRANT_MAX_LIMIT).optional().label("Amount").when("type", {
|
|
3961
|
+
is: "credit" /* Credit */,
|
|
3962
|
+
then: (s) => s.required("A credit grant needs an amount")
|
|
3963
|
+
})
|
|
3964
|
+
}).label("Plan Grant");
|
|
3965
|
+
var PaddlePriceIdsSchema = (0, import_yup40.object)({
|
|
3966
|
+
sandbox: (0, import_yup40.array)().of((0, import_yup40.string)().required()).optional().default([]).label("Sandbox Price IDs"),
|
|
3967
|
+
live: (0, import_yup40.array)().of((0, import_yup40.string)().required()).optional().default([]).label("Live Price IDs")
|
|
3968
|
+
}).label("Paddle Price IDs");
|
|
3969
|
+
var PlanSchema = (0, import_yup40.object)({
|
|
3970
|
+
/** Stable slug, e.g. `employer-pro`. The identifier everything else references. */
|
|
3971
|
+
key: (0, import_yup40.string)().trim().lowercase().required().min(2).max(64).label("Key"),
|
|
3972
|
+
name: (0, import_yup40.string)().required().min(1).max(120).label("Name"),
|
|
3973
|
+
description: (0, import_yup40.string)().optional().max(500).label("Description"),
|
|
3974
|
+
kind: (0, import_yup40.string)().oneOf(SupportedPlanKinds).required().default("subscription" /* Subscription */).label("Kind"),
|
|
3975
|
+
status: (0, import_yup40.string)().oneOf(SupportedPlanStatuses).required().default("draft" /* Draft */).label("Status"),
|
|
3976
|
+
paddlePriceIds: PaddlePriceIdsSchema.default(
|
|
3977
|
+
() => PaddlePriceIdsSchema.getDefault()
|
|
3978
|
+
),
|
|
3979
|
+
grants: (0, import_yup40.array)().of(PlanGrantSchema.required()).required().min(1, "A plan must grant at least one feature").label("Grants"),
|
|
3980
|
+
/** Display price for the pricing page. Billing is always Paddle's own figure. */
|
|
3981
|
+
price: MoneySchema.optional().default(void 0).label("Price"),
|
|
3982
|
+
/**
|
|
3983
|
+
* How often this plan bills, for the pricing page's monthly/yearly toggle and
|
|
3984
|
+
* for rendering `price` with the right suffix.
|
|
3985
|
+
*
|
|
3986
|
+
* Display only: Paddle owns the real billing interval, and this never drives a
|
|
3987
|
+
* charge. It is optional because one-off plans have no interval, and because
|
|
3988
|
+
* plans seeded before this field existed must keep validating. Treat absent on
|
|
3989
|
+
* a subscription as "monthly" at the use site rather than defaulting it here,
|
|
3990
|
+
* so an unset value stays visible as unset.
|
|
3991
|
+
*/
|
|
3992
|
+
billingPeriod: (0, import_yup40.string)().oneOf(SupportedBillingPeriods).optional().label("Billing Period"),
|
|
3993
|
+
/** Ordering on the pricing page, ascending. */
|
|
3994
|
+
sortOrder: (0, import_yup40.number)().integer().optional().default(0).label("Sort Order"),
|
|
3995
|
+
createdAt: (0, import_yup40.number)().optional().label("Created At"),
|
|
3996
|
+
updatedAt: (0, import_yup40.number)().optional().label("Updated At")
|
|
3997
|
+
}).label("Plan");
|
|
3998
|
+
var BillingSubscriptionSchema = (0, import_yup40.object)({
|
|
3999
|
+
userId: (0, import_yup40.string)().required().label("User ID"),
|
|
4000
|
+
paddleSubscriptionId: (0, import_yup40.string)().required().label("Paddle Subscription ID"),
|
|
4001
|
+
paddleCustomerId: (0, import_yup40.string)().required().label("Paddle Customer ID"),
|
|
4002
|
+
planKey: (0, import_yup40.string)().required().label("Plan Key"),
|
|
4003
|
+
priceIds: (0, import_yup40.array)().of((0, import_yup40.string)().required()).required().label("Price IDs"),
|
|
4004
|
+
status: (0, import_yup40.string)().oneOf(SupportedSubscriptionStatuses).required().label("Status"),
|
|
4005
|
+
/** Start of the period the quota is anchored to. */
|
|
4006
|
+
currentPeriodStart: (0, import_yup40.number)().optional().label("Current Period Start"),
|
|
4007
|
+
currentPeriodEnd: (0, import_yup40.number)().optional().label("Current Period End"),
|
|
4008
|
+
/** Set when a cancellation is scheduled; access continues until the period ends. */
|
|
4009
|
+
canceledAt: (0, import_yup40.number)().optional().label("Canceled At"),
|
|
4010
|
+
/** `occurred_at` of the newest Paddle event applied. Guards out-of-order writes. */
|
|
4011
|
+
lastEventAt: (0, import_yup40.number)().required().default(0).label("Last Event At"),
|
|
4012
|
+
createdAt: (0, import_yup40.number)().optional().label("Created At"),
|
|
4013
|
+
updatedAt: (0, import_yup40.number)().optional().label("Updated At")
|
|
4014
|
+
}).label("Billing Subscription");
|
|
4015
|
+
var BillingTransactionSchema = (0, import_yup40.object)({
|
|
4016
|
+
userId: (0, import_yup40.string)().required().label("User ID"),
|
|
4017
|
+
paddleTransactionId: (0, import_yup40.string)().required().label("Paddle Transaction ID"),
|
|
4018
|
+
paddleCustomerId: (0, import_yup40.string)().optional().label("Paddle Customer ID"),
|
|
4019
|
+
paddleSubscriptionId: (0, import_yup40.string)().optional().label("Paddle Subscription ID"),
|
|
4020
|
+
planKey: (0, import_yup40.string)().optional().label("Plan Key"),
|
|
4021
|
+
priceIds: (0, import_yup40.array)().of((0, import_yup40.string)().required()).required().label("Price IDs"),
|
|
4022
|
+
status: (0, import_yup40.string)().oneOf(SupportedTransactionStatuses).required().label("Status"),
|
|
4023
|
+
total: MoneySchema.optional().default(void 0).label("Total"),
|
|
4024
|
+
billedAt: (0, import_yup40.number)().optional().label("Billed At"),
|
|
4025
|
+
lastEventAt: (0, import_yup40.number)().required().default(0).label("Last Event At"),
|
|
4026
|
+
createdAt: (0, import_yup40.number)().optional().label("Created At"),
|
|
4027
|
+
updatedAt: (0, import_yup40.number)().optional().label("Updated At")
|
|
4028
|
+
}).label("Billing Transaction");
|
|
4029
|
+
var FeatureEntitlementSchema = (0, import_yup40.object)({
|
|
4030
|
+
feature: (0, import_yup40.string)().required().label("Feature"),
|
|
4031
|
+
type: (0, import_yup40.string)().oneOf(SupportedGrantTypes).required().label("Grant Type"),
|
|
4032
|
+
/** Whether the feature may be used AT ALL right now. */
|
|
4033
|
+
allowed: (0, import_yup40.boolean)().required().default(false).label("Allowed"),
|
|
4034
|
+
/** Quota ceiling for the period. Absent for boolean grants. */
|
|
4035
|
+
limit: (0, import_yup40.number)().integer().optional().label("Limit"),
|
|
4036
|
+
used: (0, import_yup40.number)().integer().optional().label("Used"),
|
|
4037
|
+
/** Quota remaining plus any credit balance. Absent for boolean grants. */
|
|
4038
|
+
remaining: (0, import_yup40.number)().integer().optional().label("Remaining"),
|
|
4039
|
+
/** Non-expiring balance from one-off purchases. */
|
|
4040
|
+
credits: (0, import_yup40.number)().integer().optional().label("Credits"),
|
|
4041
|
+
/** When the quota next refreshes. */
|
|
4042
|
+
resetsAt: (0, import_yup40.number)().optional().label("Resets At")
|
|
4043
|
+
}).label("Feature Entitlement");
|
|
4044
|
+
var EntitlementDenialSchema = (0, import_yup40.object)({
|
|
4045
|
+
feature: (0, import_yup40.string)().required().label("Feature"),
|
|
4046
|
+
reason: (0, import_yup40.string)().oneOf(SupportedEntitlementDenialReasons).required().label("Reason"),
|
|
4047
|
+
limit: (0, import_yup40.number)().integer().optional().label("Limit"),
|
|
4048
|
+
used: (0, import_yup40.number)().integer().optional().label("Used"),
|
|
4049
|
+
resetsAt: (0, import_yup40.number)().optional().label("Resets At"),
|
|
4050
|
+
upgradeUrl: (0, import_yup40.string)().optional().label("Upgrade URL")
|
|
4051
|
+
}).label("Entitlement Denial");
|
|
4052
|
+
var EntitlementConsumeSchema = (0, import_yup40.object)({
|
|
4053
|
+
userId: (0, import_yup40.string)().required().label("User ID"),
|
|
4054
|
+
feature: (0, import_yup40.string)().trim().required().min(1).max(64).label("Feature"),
|
|
4055
|
+
units: (0, import_yup40.number)().integer().min(1).max(CONSUME_MAX_UNITS).required().default(1).label("Units"),
|
|
4056
|
+
/**
|
|
4057
|
+
* Caller-supplied de-duplication key. Makes a spend exactly-once across the
|
|
4058
|
+
* retries that at-least-once delivery and ordinary network failures produce.
|
|
4059
|
+
*/
|
|
4060
|
+
idempotencyKey: (0, import_yup40.string)().trim().required().min(8).max(128).label("Idempotency Key")
|
|
4061
|
+
}).label("Entitlement Consume");
|
|
4062
|
+
|
|
2799
4063
|
// src/user/user.schema.ts
|
|
2800
|
-
var
|
|
4064
|
+
var import_yup53 = require("yup");
|
|
2801
4065
|
|
|
2802
4066
|
// src/user/work-experience.schema.ts
|
|
2803
|
-
var
|
|
2804
|
-
var WorkExperienceSchema = (0,
|
|
4067
|
+
var import_yup41 = require("yup");
|
|
4068
|
+
var WorkExperienceSchema = (0, import_yup41.object)().shape({
|
|
2805
4069
|
company: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Company"),
|
|
2806
|
-
designation: (0,
|
|
4070
|
+
designation: (0, import_yup41.string)().trim().required().label("Designation"),
|
|
2807
4071
|
duration: DurationSchema({
|
|
2808
4072
|
format: "YYYY-MM",
|
|
2809
4073
|
startLabel: "Start Date",
|
|
2810
4074
|
endLabel: "End Date"
|
|
2811
4075
|
}).required().label("Duration"),
|
|
2812
|
-
description: (0,
|
|
4076
|
+
description: (0, import_yup41.string)().trim().max(3e3).optional().label("Description"),
|
|
2813
4077
|
location: LocationSchema.required().label("Location"),
|
|
2814
|
-
isRemote: (0,
|
|
4078
|
+
isRemote: (0, import_yup41.boolean)().oneOf([true, false]).default(false).label("Is Remote")
|
|
2815
4079
|
}).noUnknown().strict().label("Work Experience");
|
|
2816
4080
|
|
|
2817
4081
|
// src/user/education.schema.ts
|
|
2818
|
-
var
|
|
2819
|
-
var EducationSchema = (0,
|
|
4082
|
+
var import_yup42 = require("yup");
|
|
4083
|
+
var EducationSchema = (0, import_yup42.object)({
|
|
2820
4084
|
institute: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Institute"),
|
|
2821
|
-
course: (0,
|
|
2822
|
-
fieldOfStudy: (0,
|
|
4085
|
+
course: (0, import_yup42.string)().trim().required().label("Course"),
|
|
4086
|
+
fieldOfStudy: (0, import_yup42.string)().trim().required().label("Field of Study"),
|
|
2823
4087
|
duration: DurationSchema({
|
|
2824
4088
|
format: "YYYY-MM",
|
|
2825
4089
|
startLabel: "Start Date",
|
|
2826
4090
|
endLabel: "End Date"
|
|
2827
4091
|
}).required().label("Duration"),
|
|
2828
|
-
description: (0,
|
|
2829
|
-
isDistanceLearning: (0,
|
|
4092
|
+
description: (0, import_yup42.string)().trim().max(3e3).optional().label("Description"),
|
|
4093
|
+
isDistanceLearning: (0, import_yup42.boolean)().default(false).label("Is Distance Learning"),
|
|
2830
4094
|
location: LocationSchema.required().label("Location"),
|
|
2831
|
-
studyType: (0,
|
|
4095
|
+
studyType: (0, import_yup42.string)().oneOf(SupportedStudyTypes).trim().max(50).required().label("Study Type")
|
|
2832
4096
|
}).noUnknown().strict().label("Education");
|
|
2833
4097
|
|
|
2834
4098
|
// src/user/user-skill.schema.ts
|
|
2835
|
-
var
|
|
2836
|
-
var UserSkillSchema = (0,
|
|
2837
|
-
name: (0,
|
|
2838
|
-
proficiencyLevel: (0,
|
|
4099
|
+
var import_yup43 = require("yup");
|
|
4100
|
+
var UserSkillSchema = (0, import_yup43.object)({
|
|
4101
|
+
name: (0, import_yup43.string)().required().label("Name"),
|
|
4102
|
+
proficiencyLevel: (0, import_yup43.string)().oneOf(SupportedProficiencyLevels).required().label("Proficiency Level"),
|
|
2839
4103
|
lastUsed: dateString().format("YYYY-MM").nullable().optional().label("Last Used")
|
|
2840
4104
|
}).noUnknown().strict().label("Skill");
|
|
2841
4105
|
|
|
2842
4106
|
// src/user/project.schema.ts
|
|
2843
|
-
var
|
|
2844
|
-
var UserProjectSchema = (0,
|
|
2845
|
-
name: (0,
|
|
2846
|
-
url: (0,
|
|
2847
|
-
description: (0,
|
|
4107
|
+
var import_yup44 = require("yup");
|
|
4108
|
+
var UserProjectSchema = (0, import_yup44.object)({
|
|
4109
|
+
name: (0, import_yup44.string)().trim().max(50).required().label("Name"),
|
|
4110
|
+
url: (0, import_yup44.string)().optional().label("URL"),
|
|
4111
|
+
description: (0, import_yup44.string)().trim().min(100).max(3e3).required().label("Description"),
|
|
2848
4112
|
duration: DurationSchema({
|
|
2849
4113
|
format: "YYYY-MM",
|
|
2850
4114
|
startLabel: "Start Date",
|
|
@@ -2853,61 +4117,68 @@ var UserProjectSchema = (0, import_yup28.object)({
|
|
|
2853
4117
|
}).noUnknown().strict().label("Project");
|
|
2854
4118
|
|
|
2855
4119
|
// src/user/user-certification.schema.ts
|
|
2856
|
-
var
|
|
2857
|
-
var UserCertificationSchema = (0,
|
|
2858
|
-
name: (0,
|
|
4120
|
+
var import_yup45 = require("yup");
|
|
4121
|
+
var UserCertificationSchema = (0, import_yup45.object)({
|
|
4122
|
+
name: (0, import_yup45.string)().trim().max(100).required().label("Name"),
|
|
2859
4123
|
// TODO: Add validation for authority
|
|
2860
|
-
authority: (0,
|
|
2861
|
-
licenseNumber: (0,
|
|
4124
|
+
authority: (0, import_yup45.string)().trim().max(100).required().label("Authority"),
|
|
4125
|
+
licenseNumber: (0, import_yup45.string)().trim().max(50).optional().label("License Number"),
|
|
2862
4126
|
duration: DurationSchema({
|
|
2863
4127
|
format: "YYYY-MM",
|
|
2864
4128
|
startLabel: "Start Date",
|
|
2865
4129
|
endLabel: "End Date"
|
|
2866
4130
|
}).optional().nullable().label("Duration"),
|
|
2867
|
-
url: (0,
|
|
4131
|
+
url: (0, import_yup45.string)().optional().label("URL")
|
|
2868
4132
|
}).noUnknown().strict().label("Certification");
|
|
2869
4133
|
|
|
2870
4134
|
// src/user/user-interest.schema.ts
|
|
2871
|
-
var
|
|
2872
|
-
var UserInterestSchema = (0,
|
|
4135
|
+
var import_yup46 = require("yup");
|
|
4136
|
+
var UserInterestSchema = (0, import_yup46.object)({
|
|
4137
|
+
/** The taxonomy entry's id. */
|
|
4138
|
+
id: (0, import_yup46.string)().trim().required().label("Interest Id"),
|
|
4139
|
+
/** Display name, e.g. "Cloud Computing". */
|
|
4140
|
+
name: (0, import_yup46.string)().trim().required().label("Interest"),
|
|
4141
|
+
/** Material-symbols icon name, e.g. "cloud". */
|
|
4142
|
+
logo: (0, import_yup46.string)().trim().required().label("Interest Logo")
|
|
4143
|
+
}).required().label("Interest");
|
|
2873
4144
|
|
|
2874
4145
|
// src/user/user-language.schema.ts
|
|
2875
|
-
var
|
|
2876
|
-
var UserLanguageSchema = (0,
|
|
2877
|
-
name: (0,
|
|
2878
|
-
proficiencyLevel: (0,
|
|
4146
|
+
var import_yup47 = require("yup");
|
|
4147
|
+
var UserLanguageSchema = (0, import_yup47.object)({
|
|
4148
|
+
name: (0, import_yup47.string)().trim().required().label("Name"),
|
|
4149
|
+
proficiencyLevel: (0, import_yup47.string)().oneOf(SupportedProficiencyLevels).trim().max(50).required().label("Proficiency Level")
|
|
2879
4150
|
}).noUnknown().strict().label("Language");
|
|
2880
4151
|
|
|
2881
4152
|
// src/user/user-additional-info.schema.ts
|
|
2882
|
-
var
|
|
2883
|
-
var UserAdditionalInfoSchema = (0,
|
|
2884
|
-
title: (0,
|
|
2885
|
-
description: (0,
|
|
4153
|
+
var import_yup48 = require("yup");
|
|
4154
|
+
var UserAdditionalInfoSchema = (0, import_yup48.object)({
|
|
4155
|
+
title: (0, import_yup48.string)().trim().max(60).required().label("Title"),
|
|
4156
|
+
description: (0, import_yup48.string)().trim().max(1e3).required().label("Description")
|
|
2886
4157
|
}).noUnknown().strict().label("User Additional Info");
|
|
2887
4158
|
|
|
2888
4159
|
// src/user/general-detail.schema.ts
|
|
2889
|
-
var
|
|
2890
|
-
var UserGeneralDetailSchema = (0,
|
|
2891
|
-
id: (0,
|
|
2892
|
-
name: (0,
|
|
2893
|
-
first: (0,
|
|
2894
|
-
last: (0,
|
|
4160
|
+
var import_yup49 = require("yup");
|
|
4161
|
+
var UserGeneralDetailSchema = (0, import_yup49.object)({
|
|
4162
|
+
id: (0, import_yup49.string)().optional().label("ID"),
|
|
4163
|
+
name: (0, import_yup49.object)().shape({
|
|
4164
|
+
first: (0, import_yup49.string)().trim().max(50).required().label("First Name"),
|
|
4165
|
+
last: (0, import_yup49.string)().trim().max(50).optional().label("Last Name")
|
|
2895
4166
|
}).required().label("Name"),
|
|
2896
|
-
headline: (0,
|
|
2897
|
-
image: (0,
|
|
4167
|
+
headline: (0, import_yup49.string)().trim().max(200).optional().label("Headline"),
|
|
4168
|
+
image: (0, import_yup49.string)().optional().label("Image"),
|
|
2898
4169
|
// The DISPLAYED avatar (`image`) is a flattened export with any status frame
|
|
2899
4170
|
// ("#OpenToWork", …) already burned into the pixels. `imageOriginal` keeps the
|
|
2900
4171
|
// un-framed source the user uploaded, so the image builder can reopen it and
|
|
2901
4172
|
// apply a DIFFERENT frame instead of stacking a second one on top of the first.
|
|
2902
4173
|
// Optional and absent on every pre-existing user: treat a missing value as
|
|
2903
4174
|
// "only the flattened image is available".
|
|
2904
|
-
imageOriginal: (0,
|
|
2905
|
-
aboutMe: (0,
|
|
2906
|
-
email: (0,
|
|
2907
|
-
mobile: (0,
|
|
2908
|
-
emailVerified: (0,
|
|
2909
|
-
mobileVerified: (0,
|
|
2910
|
-
experienceLevel: (0,
|
|
4175
|
+
imageOriginal: (0, import_yup49.string)().optional().label("Original Image"),
|
|
4176
|
+
aboutMe: (0, import_yup49.string)().trim().max(3e3).optional().label("About Me"),
|
|
4177
|
+
email: (0, import_yup49.string)().required().label("Email"),
|
|
4178
|
+
mobile: (0, import_yup49.string)().nullable().optional().label("Mobile"),
|
|
4179
|
+
emailVerified: (0, import_yup49.string)().nullable().optional().label("Email Verified"),
|
|
4180
|
+
mobileVerified: (0, import_yup49.string)().nullable().optional().label("Mobile Verified"),
|
|
4181
|
+
experienceLevel: (0, import_yup49.string)().oneOf(SupportedExperienceLevels).required().label("Experience level"),
|
|
2911
4182
|
/**
|
|
2912
4183
|
* The user's HIGHEST qualification, e.g. `bachelor`.
|
|
2913
4184
|
*
|
|
@@ -2926,7 +4197,7 @@ var UserGeneralDetailSchema = (0, import_yup33.object)({
|
|
|
2926
4197
|
* UNCONSTRAINED on education rather than filtering the user out — the same
|
|
2927
4198
|
* opt-out reasoning `notificationPrefs` uses.
|
|
2928
4199
|
*/
|
|
2929
|
-
educationLevel: (0,
|
|
4200
|
+
educationLevel: (0, import_yup49.string)().oneOf(SupportedEducationLevels).optional().label("Education level"),
|
|
2930
4201
|
location: LocationSchema.required().label("Location"),
|
|
2931
4202
|
/**
|
|
2932
4203
|
* Where the account was created, captured once and never rewritten.
|
|
@@ -2942,38 +4213,38 @@ var UserGeneralDetailSchema = (0, import_yup33.object)({
|
|
|
2942
4213
|
* - `location` derived from the user's self-entered profile location
|
|
2943
4214
|
* - `default` assumed (no signal available); presumption, not evidence
|
|
2944
4215
|
*/
|
|
2945
|
-
signupContext: (0,
|
|
4216
|
+
signupContext: (0, import_yup49.object)().shape({
|
|
2946
4217
|
// ISO 3166-1 alpha-2, uppercase — the shape `cf-ipcountry` returns, so
|
|
2947
4218
|
// measured and inferred values are directly comparable.
|
|
2948
|
-
country: (0,
|
|
2949
|
-
locale: (0,
|
|
2950
|
-
source: (0,
|
|
2951
|
-
at: (0,
|
|
4219
|
+
country: (0, import_yup49.string)().trim().uppercase().length(2).required().label("Signup Country"),
|
|
4220
|
+
locale: (0, import_yup49.string)().trim().required().label("Signup Locale"),
|
|
4221
|
+
source: (0, import_yup49.string)().oneOf(SupportedSignupContextSources).required().label("Signup Context Source"),
|
|
4222
|
+
at: (0, import_yup49.number)().required().label("Signup Context At")
|
|
2952
4223
|
}).optional().default(void 0).label("Signup Context"),
|
|
2953
|
-
profileVisibility: (0,
|
|
4224
|
+
profileVisibility: (0, import_yup49.string)().oneOf(SupportedUserProfileVisibilities).default("public" /* Public */).label("Profile Visibility")
|
|
2954
4225
|
}).noUnknown().strict().label("General Detail");
|
|
2955
4226
|
|
|
2956
4227
|
// src/user/user-job-preferences.schema.ts
|
|
2957
|
-
var
|
|
2958
|
-
var UserJobPreferencesSchema = (0,
|
|
4228
|
+
var import_yup50 = require("yup");
|
|
4229
|
+
var UserJobPreferencesSchema = (0, import_yup50.object)({
|
|
2959
4230
|
/**
|
|
2960
4231
|
* Whether the job seeker is openly signalling availability. Surfaces the
|
|
2961
4232
|
* "Open to work" badge on the public profile and boosts visibility in
|
|
2962
4233
|
* recruiter search. Defaults to `false` - users must opt in.
|
|
2963
4234
|
*/
|
|
2964
|
-
openToWork: (0,
|
|
4235
|
+
openToWork: (0, import_yup50.boolean)().default(false).label("Open to Work"),
|
|
2965
4236
|
/**
|
|
2966
4237
|
* How urgently the user is looking for their next job. Drives match scoring
|
|
2967
4238
|
* and can downgrade the visibility of expired or low-relevance postings for
|
|
2968
4239
|
* users in `passively_browsing` mode.
|
|
2969
4240
|
*/
|
|
2970
|
-
jobSearchUrgency: (0,
|
|
4241
|
+
jobSearchUrgency: (0, import_yup50.string)().oneOf(SupportedJobSearchUrgencies).required().label("Job Search Urgency"),
|
|
2971
4242
|
/**
|
|
2972
4243
|
* Locations the user wants to work in. Stores the full `LocationSchema`
|
|
2973
4244
|
* shape (country, city, state, geo, etc.) as returned by the locations
|
|
2974
4245
|
* autocomplete API.
|
|
2975
4246
|
*/
|
|
2976
|
-
jobLocations: (0,
|
|
4247
|
+
jobLocations: (0, import_yup50.array)().of(LocationSchema.required().label("Job Location")).min(1, "Pick at least one preferred location.").required().label("Job Locations"),
|
|
2977
4248
|
/**
|
|
2978
4249
|
* Seniority levels the user wants jobs to be matched against. Multi-select
|
|
2979
4250
|
* (up to 2) for borderline candidates.
|
|
@@ -2984,7 +4255,7 @@ var UserJobPreferencesSchema = (0, import_yup34.object)({
|
|
|
2984
4255
|
* Both reuse the same `ExperienceLevel` taxonomy from `common.constant.ts`
|
|
2985
4256
|
* so search/match logic doesn't have to translate between two vocabularies.
|
|
2986
4257
|
*/
|
|
2987
|
-
targetExperienceLevels: (0,
|
|
4258
|
+
targetExperienceLevels: (0, import_yup50.array)().of((0, import_yup50.string)().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"),
|
|
2988
4259
|
/**
|
|
2989
4260
|
* Specializations the user is searching for - free-form titles.
|
|
2990
4261
|
*
|
|
@@ -2993,19 +4264,19 @@ var UserJobPreferencesSchema = (0, import_yup34.object)({
|
|
|
2993
4264
|
* surfaces a curated "Popular Roles" picker for discovery, but the user
|
|
2994
4265
|
* can ultimately add any title; matching/normalization happens downstream.
|
|
2995
4266
|
*/
|
|
2996
|
-
jobRoles: (0,
|
|
4267
|
+
jobRoles: (0, import_yup50.array)().of((0, import_yup50.string)().trim().max(120).required()).min(1, "Pick at least one role.").required().label("Job Roles"),
|
|
2997
4268
|
/**
|
|
2998
4269
|
* Minimum acceptable annual base salary, in `minSalaryCurrency`.
|
|
2999
4270
|
* Stored as an integer in the currency's major unit (e.g. dollars, not cents).
|
|
3000
4271
|
* Defaults to 0 ("any salary") rather than being optional - every job-seeker
|
|
3001
4272
|
* has a floor, even if it's zero.
|
|
3002
4273
|
*/
|
|
3003
|
-
minSalary: (0,
|
|
3004
|
-
minSalaryCurrency: (0,
|
|
4274
|
+
minSalary: (0, import_yup50.number)().integer().min(MIN_SALARY_LOWER_BOUND).max(MIN_SALARY_UPPER_BOUND).default(0).required().label("Minimum Salary"),
|
|
4275
|
+
minSalaryCurrency: (0, import_yup50.string)().oneOf(SupportedSalaryCurrencies).default("USD").required().label("Minimum Salary Currency"),
|
|
3005
4276
|
/**
|
|
3006
4277
|
* Marketing-attribution capture from the final onboarding step.
|
|
3007
4278
|
*/
|
|
3008
|
-
referralSource: (0,
|
|
4279
|
+
referralSource: (0, import_yup50.string)().oneOf(SupportedReferralSources).required().label("Referral Source"),
|
|
3009
4280
|
/**
|
|
3010
4281
|
* Resumes the user has uploaded. Embedded directly on the user document
|
|
3011
4282
|
* (capped at 5) - they're cheap to denormalize, every wizard step that
|
|
@@ -3019,25 +4290,25 @@ var UserJobPreferencesSchema = (0, import_yup34.object)({
|
|
|
3019
4290
|
* AI resume-builder flow - onboarding uploads land here, AI-built resumes
|
|
3020
4291
|
* live in their own collection.
|
|
3021
4292
|
*/
|
|
3022
|
-
resumes: (0,
|
|
3023
|
-
(0,
|
|
4293
|
+
resumes: (0, import_yup50.array)().of(
|
|
4294
|
+
(0, import_yup50.object)({
|
|
3024
4295
|
// Stable id assigned on upload - used to address a specific entry
|
|
3025
4296
|
// for delete / set-primary operations without exposing the GCS URL
|
|
3026
4297
|
// as a route key.
|
|
3027
|
-
id: (0,
|
|
3028
|
-
url: (0,
|
|
3029
|
-
filename: (0,
|
|
3030
|
-
sizeBytes: (0,
|
|
3031
|
-
mimeType: (0,
|
|
3032
|
-
uploadedAt: (0,
|
|
3033
|
-
isPrimary: (0,
|
|
4298
|
+
id: (0, import_yup50.string)().required().label("ID"),
|
|
4299
|
+
url: (0, import_yup50.string)().required().label("Resume URL"),
|
|
4300
|
+
filename: (0, import_yup50.string)().trim().max(255).optional().label("Filename"),
|
|
4301
|
+
sizeBytes: (0, import_yup50.number)().integer().min(0).optional().label("Size (bytes)"),
|
|
4302
|
+
mimeType: (0, import_yup50.string)().trim().max(120).optional().label("MIME Type"),
|
|
4303
|
+
uploadedAt: (0, import_yup50.date)().required().label("Uploaded At"),
|
|
4304
|
+
isPrimary: (0, import_yup50.boolean)().default(false).label("Is Primary")
|
|
3034
4305
|
}).noUnknown().strict().label("Resume")
|
|
3035
4306
|
).max(5, "You can keep at most 5 resumes on file.").default([]).label("Resumes"),
|
|
3036
4307
|
/**
|
|
3037
4308
|
* Set when the user finishes the wizard's last step. Acts as the gate the
|
|
3038
4309
|
* onboarding hook checks to redirect users who haven't completed setup.
|
|
3039
4310
|
*/
|
|
3040
|
-
onboardingCompletedAt: (0,
|
|
4311
|
+
onboardingCompletedAt: (0, import_yup50.date)().nullable().optional().label("Onboarding Completed At"),
|
|
3041
4312
|
/**
|
|
3042
4313
|
* Parser-suggested values for wizard fields, written by the resume-upload
|
|
3043
4314
|
* flow. Every parser-derived field lands here - including profile-shaped
|
|
@@ -3057,92 +4328,92 @@ var UserJobPreferencesSchema = (0, import_yup34.object)({
|
|
|
3057
4328
|
* APIs. If `user.<field>` is empty there, the correct behavior is empty,
|
|
3058
4329
|
* because the user has not confirmed it yet.
|
|
3059
4330
|
*/
|
|
3060
|
-
pendingPrefill: (0,
|
|
3061
|
-
headline: (0,
|
|
3062
|
-
aboutMe: (0,
|
|
3063
|
-
mobile: (0,
|
|
4331
|
+
pendingPrefill: (0, import_yup50.object)({
|
|
4332
|
+
headline: (0, import_yup50.string)().trim().optional().label("Pending Headline"),
|
|
4333
|
+
aboutMe: (0, import_yup50.string)().trim().optional().label("Pending About Me"),
|
|
4334
|
+
mobile: (0, import_yup50.string)().trim().optional().label("Pending Mobile"),
|
|
3064
4335
|
location: LocationSchema.optional().default(void 0).label("Pending Location"),
|
|
3065
|
-
experienceLevel: (0,
|
|
3066
|
-
socialAccounts: (0,
|
|
3067
|
-
workExperiences: (0,
|
|
3068
|
-
educations: (0,
|
|
4336
|
+
experienceLevel: (0, import_yup50.string)().oneOf(SupportedExperienceLevels).optional().label("Pending Experience Level"),
|
|
4337
|
+
socialAccounts: (0, import_yup50.array)().of(SocialAccountSchema.required()).optional().label("Pending Social Accounts"),
|
|
4338
|
+
workExperiences: (0, import_yup50.array)().of(WorkExperienceSchema.required()).optional().label("Pending Work Experiences"),
|
|
4339
|
+
educations: (0, import_yup50.array)().of(EducationSchema.required()).optional().label("Pending Educations")
|
|
3069
4340
|
}).nullable().optional().default(void 0).label("Pending Prefill")
|
|
3070
4341
|
}).noUnknown().strict().label("User Job Preferences Schema");
|
|
3071
4342
|
|
|
3072
4343
|
// src/user/user-recruiter-profile.schema.ts
|
|
3073
|
-
var
|
|
3074
|
-
var RecruiterPageLinkSchema = (0,
|
|
4344
|
+
var import_yup51 = require("yup");
|
|
4345
|
+
var RecruiterPageLinkSchema = (0, import_yup51.object)({
|
|
3075
4346
|
page: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Company Page"),
|
|
3076
|
-
jobTitle: (0,
|
|
4347
|
+
jobTitle: (0, import_yup51.string)().trim().max(100).optional().label("Job Title")
|
|
3077
4348
|
}).noUnknown().strict().label("Recruiter Page Link");
|
|
3078
|
-
var UserRecruiterProfileSchema = (0,
|
|
3079
|
-
recruiterProfile: (0,
|
|
3080
|
-
hiringFor: (0,
|
|
4349
|
+
var UserRecruiterProfileSchema = (0, import_yup51.object)({
|
|
4350
|
+
recruiterProfile: (0, import_yup51.object)({
|
|
4351
|
+
hiringFor: (0, import_yup51.array)().of(RecruiterPageLinkSchema).default([]).label("Hiring For")
|
|
3081
4352
|
}).optional().default(void 0).label("Recruiter Profile")
|
|
3082
4353
|
}).noUnknown().strict().label("User Recruiter Profile");
|
|
3083
4354
|
|
|
3084
4355
|
// src/user/user-coordinator-profile.schema.ts
|
|
3085
|
-
var
|
|
3086
|
-
var CoordinatorPageLinkSchema = (0,
|
|
4356
|
+
var import_yup52 = require("yup");
|
|
4357
|
+
var CoordinatorPageLinkSchema = (0, import_yup52.object)({
|
|
3087
4358
|
page: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Institute Page"),
|
|
3088
|
-
jobTitle: (0,
|
|
4359
|
+
jobTitle: (0, import_yup52.string)().trim().max(100).optional().label("Job Title")
|
|
3089
4360
|
}).noUnknown().strict().label("Coordinator Page Link");
|
|
3090
|
-
var UserCoordinatorProfileSchema = (0,
|
|
3091
|
-
coordinatorProfile: (0,
|
|
3092
|
-
coordinatesAt: (0,
|
|
4361
|
+
var UserCoordinatorProfileSchema = (0, import_yup52.object)({
|
|
4362
|
+
coordinatorProfile: (0, import_yup52.object)({
|
|
4363
|
+
coordinatesAt: (0, import_yup52.array)().of(CoordinatorPageLinkSchema).default([]).label("Coordinates At")
|
|
3093
4364
|
}).optional().default(void 0).label("Coordinator Profile")
|
|
3094
4365
|
}).noUnknown().strict().label("User Coordinator Profile");
|
|
3095
4366
|
|
|
3096
4367
|
// src/user/user.schema.ts
|
|
3097
|
-
var UserSchema = (0,
|
|
4368
|
+
var UserSchema = (0, import_yup53.object)({
|
|
3098
4369
|
/**
|
|
3099
4370
|
* Related auth account id from auth Server (e.g. Better auth https://auth.thejob.dev)
|
|
3100
4371
|
*/
|
|
3101
|
-
authAccountId: (0,
|
|
4372
|
+
authAccountId: (0, import_yup53.string)().required().label("Auth Account ID"),
|
|
3102
4373
|
/**
|
|
3103
4374
|
* Social media information about the user (e.g. LinkedIn, GitHub, etc.)
|
|
3104
4375
|
*/
|
|
3105
|
-
socialAccounts: (0,
|
|
4376
|
+
socialAccounts: (0, import_yup53.array)().of(SocialAccountSchema).default([]).label("Social Accounts"),
|
|
3106
4377
|
/**
|
|
3107
4378
|
* Work experience information about the user
|
|
3108
4379
|
*/
|
|
3109
|
-
workExperiences: (0,
|
|
4380
|
+
workExperiences: (0, import_yup53.array)().of(WorkExperienceSchema).required().default([]).label("Work Experiences"),
|
|
3110
4381
|
/**
|
|
3111
4382
|
* Education information about the user
|
|
3112
4383
|
*/
|
|
3113
|
-
educations: (0,
|
|
4384
|
+
educations: (0, import_yup53.array)().of(EducationSchema).required().default([]).label("Educations"),
|
|
3114
4385
|
/**
|
|
3115
4386
|
* Skills information about the user
|
|
3116
4387
|
*/
|
|
3117
|
-
skills: (0,
|
|
4388
|
+
skills: (0, import_yup53.array)().of(UserSkillSchema).required().default([]).label("Skills"),
|
|
3118
4389
|
/**
|
|
3119
4390
|
* Projects information about the user
|
|
3120
4391
|
*/
|
|
3121
|
-
projects: (0,
|
|
4392
|
+
projects: (0, import_yup53.array)().of(UserProjectSchema).default([]).label("Projects"),
|
|
3122
4393
|
/**
|
|
3123
4394
|
* Certifications information about the user
|
|
3124
4395
|
*/
|
|
3125
|
-
certifications: (0,
|
|
4396
|
+
certifications: (0, import_yup53.array)().of(UserCertificationSchema).default([]).label("Certifications"),
|
|
3126
4397
|
/**
|
|
3127
4398
|
* Interests information about the user.
|
|
3128
4399
|
*/
|
|
3129
|
-
interests: (0,
|
|
4400
|
+
interests: (0, import_yup53.array)().of(UserInterestSchema).optional().default([]).label("Interests"),
|
|
3130
4401
|
/**
|
|
3131
4402
|
* Languages information about the user
|
|
3132
4403
|
*/
|
|
3133
|
-
languages: (0,
|
|
4404
|
+
languages: (0, import_yup53.array)().of(UserLanguageSchema).required().default([]).label("Languages"),
|
|
3134
4405
|
/**
|
|
3135
4406
|
* Additional information about the user
|
|
3136
4407
|
*/
|
|
3137
|
-
additionalInfo: (0,
|
|
4408
|
+
additionalInfo: (0, import_yup53.array)().of(UserAdditionalInfoSchema).default([]).label("Additional Information"),
|
|
3138
4409
|
/**
|
|
3139
4410
|
* Status of the user account
|
|
3140
4411
|
*/
|
|
3141
|
-
status: (0,
|
|
4412
|
+
status: (0, import_yup53.string)().oneOf(SupportedUserStatuses).required().label("Status"),
|
|
3142
4413
|
/**
|
|
3143
4414
|
* Roles assigned to the user
|
|
3144
4415
|
*/
|
|
3145
|
-
roles: (0,
|
|
4416
|
+
roles: (0, import_yup53.array)().of((0, import_yup53.string)().oneOf(SupportedUserRoles)).required().default(DefaultUserRoles).label("Roles"),
|
|
3146
4417
|
/**
|
|
3147
4418
|
* Explicit consent to receive marketing email (newsletters, job alerts by
|
|
3148
4419
|
* broadcast, product updates). Marketing sends are HARD-GATED on
|
|
@@ -3154,18 +4425,18 @@ var UserSchema = (0, import_yup37.object)({
|
|
|
3154
4425
|
* unsubscribe sets `subscribed=false` so consent and the email-service
|
|
3155
4426
|
* suppression list stay consistent. Defaults to NOT subscribed (opt-in).
|
|
3156
4427
|
*/
|
|
3157
|
-
marketingConsent: (0,
|
|
3158
|
-
subscribed: (0,
|
|
4428
|
+
marketingConsent: (0, import_yup53.object)({
|
|
4429
|
+
subscribed: (0, import_yup53.boolean)().default(false).label("Subscribed to marketing email"),
|
|
3159
4430
|
/** Epoch ms of the last consent change (opt-in or opt-out). */
|
|
3160
|
-
updatedAt: (0,
|
|
4431
|
+
updatedAt: (0, import_yup53.number)().optional().label("Consent updated at"),
|
|
3161
4432
|
/** Where the consent change originated, e.g. `onboarding`, `unsubscribe`, `settings`. */
|
|
3162
|
-
source: (0,
|
|
4433
|
+
source: (0, import_yup53.string)().optional().label("Consent source"),
|
|
3163
4434
|
/**
|
|
3164
4435
|
* How often the user receives job-alert emails. `off` disables job alerts
|
|
3165
4436
|
* only; other marketing stays governed by `subscribed`. Unset means the
|
|
3166
4437
|
* user has not chosen a cadence yet.
|
|
3167
4438
|
*/
|
|
3168
|
-
jobAlertFrequency: (0,
|
|
4439
|
+
jobAlertFrequency: (0, import_yup53.string)().oneOf(SupportedJobAlertFrequencies).optional().label("Job alert frequency")
|
|
3169
4440
|
}).default({ subscribed: false }).label("Marketing Consent"),
|
|
3170
4441
|
/**
|
|
3171
4442
|
* Per-channel toggles for NOTIFICATION email — mail about activity on the
|
|
@@ -3180,31 +4451,142 @@ var UserSchema = (0, import_yup37.object)({
|
|
|
3180
4451
|
*
|
|
3181
4452
|
* Transactional mail (magic-link, verification) ignores this object entirely.
|
|
3182
4453
|
*/
|
|
3183
|
-
notificationPrefs: (0,
|
|
3184
|
-
groupActivity: (0,
|
|
3185
|
-
jobActivity: (0,
|
|
3186
|
-
applicationUpdates: (0,
|
|
3187
|
-
productUpdates: (0,
|
|
4454
|
+
notificationPrefs: (0, import_yup53.object)({
|
|
4455
|
+
groupActivity: (0, import_yup53.boolean)().default(true).label("Group activity email"),
|
|
4456
|
+
jobActivity: (0, import_yup53.boolean)().default(true).label("Job activity email"),
|
|
4457
|
+
applicationUpdates: (0, import_yup53.boolean)().default(true).label("Application update email"),
|
|
4458
|
+
productUpdates: (0, import_yup53.boolean)().default(false).label("Product update email")
|
|
3188
4459
|
}).default({
|
|
3189
4460
|
groupActivity: true,
|
|
3190
4461
|
jobActivity: true,
|
|
3191
4462
|
applicationUpdates: true,
|
|
3192
4463
|
productUpdates: false
|
|
3193
4464
|
}).label("Notification Preferences"),
|
|
4465
|
+
/**
|
|
4466
|
+
* Consent to receive WEB PUSH notifications.
|
|
4467
|
+
*
|
|
4468
|
+
* Push is a TRANSPORT, not a category: it carries the same notifications
|
|
4469
|
+
* `notificationPrefs` already governs, delivered to a device instead of an
|
|
4470
|
+
* inbox. That is why no push value is added to `NotificationType` — the user
|
|
4471
|
+
* chooses whether push is used at all, and WHICH kinds are worth pushing
|
|
4472
|
+
* stays a product decision.
|
|
4473
|
+
*
|
|
4474
|
+
* Opt-IN, and it must not be folded into `notificationPrefs`: those are
|
|
4475
|
+
* opt-OUT and read `!== false`, so an absent value means ENABLED. Applied to
|
|
4476
|
+
* push, every user who predates this field would read as having granted
|
|
4477
|
+
* permission they were never asked for. This mirrors `marketingConsent`
|
|
4478
|
+
* instead, which is the opt-in shape in this file, down to the provenance
|
|
4479
|
+
* fields.
|
|
4480
|
+
*
|
|
4481
|
+
* Consent alone never delivers anything: a push send also needs a live
|
|
4482
|
+
* subscription (see `PushSubscriptionSchema`). This flag is the user's
|
|
4483
|
+
* intent, the subscription is the browser grant, and both are required.
|
|
4484
|
+
*/
|
|
4485
|
+
pushConsent: (0, import_yup53.object)({
|
|
4486
|
+
subscribed: (0, import_yup53.boolean)().default(false).label("Subscribed to push notifications"),
|
|
4487
|
+
/** Epoch ms of the last consent change (opt-in or opt-out). */
|
|
4488
|
+
updatedAt: (0, import_yup53.number)().optional().label("Push consent updated at"),
|
|
4489
|
+
/** Where the change originated, e.g. `onboarding`, `settings`, `prompt`. */
|
|
4490
|
+
source: (0, import_yup53.string)().optional().label("Push consent source")
|
|
4491
|
+
}).default({ subscribed: false }).label("Push Consent"),
|
|
3194
4492
|
/**
|
|
3195
4493
|
* Vector embedding of the user's profile for semantic/hybrid search.
|
|
3196
4494
|
* Generated from a structured summary of skills, experience, education, etc.
|
|
3197
4495
|
* Only generated for public profiles with sufficient completeness (≥60%).
|
|
3198
4496
|
*/
|
|
3199
|
-
embedding: (0,
|
|
3200
|
-
vector: (0,
|
|
3201
|
-
model: (0,
|
|
3202
|
-
}).nullable().optional().label("Embedding")
|
|
4497
|
+
embedding: (0, import_yup53.object)({
|
|
4498
|
+
vector: (0, import_yup53.array)((0, import_yup53.number)().required()).optional().label("Embedding vector"),
|
|
4499
|
+
model: (0, import_yup53.string)().optional().label("Embedding model")
|
|
4500
|
+
}).nullable().optional().label("Embedding"),
|
|
4501
|
+
/**
|
|
4502
|
+
* Epoch ms the account was created, assigned by the storing service.
|
|
4503
|
+
*
|
|
4504
|
+
* Declared here, matching `GroupSchema`, because it is not merely stored: all
|
|
4505
|
+
* 103,608 user documents carry it, `UserFilterSchema` filters on it
|
|
4506
|
+
* (`createdAtWithinDays` / `createdAtOlderThanDays`), and the admin grid sorts
|
|
4507
|
+
* and displays it. It was absent, so a consumer deriving its row type from
|
|
4508
|
+
* this schema could not name the column it renders, and this object is
|
|
4509
|
+
* `.noUnknown().strict()` — an undeclared key is stripped on validation.
|
|
4510
|
+
*
|
|
4511
|
+
* `updatedAt` is deliberately NOT added alongside it: nothing derives from it
|
|
4512
|
+
* yet, and an optional field is only safe to add when something needs it.
|
|
4513
|
+
*/
|
|
4514
|
+
createdAt: (0, import_yup53.number)().optional().label("Created At")
|
|
3203
4515
|
}).concat(UserGeneralDetailSchema).concat(UserJobPreferencesSchema).concat(UserRecruiterProfileSchema).concat(UserCoordinatorProfileSchema).noUnknown().strict().label("User Schema");
|
|
3204
4516
|
|
|
4517
|
+
// src/user/push-subscription.schema.ts
|
|
4518
|
+
var import_yup54 = require("yup");
|
|
4519
|
+
var PushSubscriptionSchema = (0, import_yup54.object)({
|
|
4520
|
+
/**
|
|
4521
|
+
* The user this subscription belongs to, by the `shortId` every service
|
|
4522
|
+
* keys a user on, matching `UserSchema.shortId`.
|
|
4523
|
+
*
|
|
4524
|
+
* NOT the Better Auth account id. That id is meaningful only inside the auth
|
|
4525
|
+
* service, and owning these rows by one would force the service that sends
|
|
4526
|
+
* push to translate an id it has no other use for. The signed-in browser
|
|
4527
|
+
* gets its own shortId straight off the JWT claim.
|
|
4528
|
+
*
|
|
4529
|
+
* Named `userShortId` rather than `shortId` because a stored row has a
|
|
4530
|
+
* `shortId` of its OWN (see `PushSubscriptionRecord`); one field cannot be
|
|
4531
|
+
* both the row's id and its owner's.
|
|
4532
|
+
*/
|
|
4533
|
+
userShortId: (0, import_yup54.string)().required().label("User Short ID"),
|
|
4534
|
+
/**
|
|
4535
|
+
* The push service URL the browser issued. Vendor-specific (FCM, Mozilla,
|
|
4536
|
+
* Apple) and effectively unbounded in length, so it carries no length cap.
|
|
4537
|
+
*
|
|
4538
|
+
* This is the IDENTITY of a subscription: it is what a re-subscribing
|
|
4539
|
+
* browser sends back unchanged, and what a 404/410 response identifies as
|
|
4540
|
+
* dead. Upserts and deletes both key on it.
|
|
4541
|
+
*/
|
|
4542
|
+
endpoint: (0, import_yup54.string)().required().label("Push Endpoint"),
|
|
4543
|
+
/**
|
|
4544
|
+
* Encryption material from the browser. Web Push payloads are encrypted to
|
|
4545
|
+
* these keys, so a row missing either one cannot be delivered to at all.
|
|
4546
|
+
*/
|
|
4547
|
+
keys: (0, import_yup54.object)({
|
|
4548
|
+
/** Client public key, base64url. */
|
|
4549
|
+
p256dh: (0, import_yup54.string)().required().label("P256DH Key"),
|
|
4550
|
+
/** Client auth secret, base64url. */
|
|
4551
|
+
auth: (0, import_yup54.string)().required().label("Auth Secret")
|
|
4552
|
+
}).required().label("Push Keys"),
|
|
4553
|
+
/**
|
|
4554
|
+
* Epoch ms the push service says the subscription expires, when it says
|
|
4555
|
+
* anything at all. Usually absent — most browsers return null — so this is
|
|
4556
|
+
* informational and never the basis for pruning. A 404/410 on send is the
|
|
4557
|
+
* only reliable death signal.
|
|
4558
|
+
*/
|
|
4559
|
+
expirationTime: (0, import_yup54.number)().nullable().optional().label("Expiration Time"),
|
|
4560
|
+
/**
|
|
4561
|
+
* The subscribing browser's user agent, kept so a user can recognise their
|
|
4562
|
+
* own devices in a "where you receive notifications" list. Display only:
|
|
4563
|
+
* never parsed, and never used to decide delivery.
|
|
4564
|
+
*/
|
|
4565
|
+
userAgent: (0, import_yup54.string)().optional().label("User Agent"),
|
|
4566
|
+
/** Epoch ms the subscription was first registered. */
|
|
4567
|
+
createdAt: (0, import_yup54.number)().optional().label("Created At"),
|
|
4568
|
+
/**
|
|
4569
|
+
* Epoch ms this subscription was last confirmed live, refreshed when the
|
|
4570
|
+
* browser re-registers it. A subscription that stops being refreshed is a
|
|
4571
|
+
* device that stopped visiting, which is useful for pruning rows that never
|
|
4572
|
+
* fail loudly enough to be caught by a 410.
|
|
4573
|
+
*/
|
|
4574
|
+
lastSeenAt: (0, import_yup54.number)().optional().label("Last Seen At"),
|
|
4575
|
+
/**
|
|
4576
|
+
* Epoch ms the owner explicitly signed out on this device, or absent.
|
|
4577
|
+
*
|
|
4578
|
+
* A PAUSE, not a removal: the sender skips flagged rows, so a shared device
|
|
4579
|
+
* stops receiving the previous user's notifications, while the same user
|
|
4580
|
+
* signing back in clears the flag with no prompt. A flag rather than a delete
|
|
4581
|
+
* so that a MISSING row keeps one meaning to the browser ("stop, this device
|
|
4582
|
+
* was removed"), which it acts on by dropping its own subscription.
|
|
4583
|
+
*/
|
|
4584
|
+
signedOutAt: (0, import_yup54.number)().nullable().optional().label("Signed Out At")
|
|
4585
|
+
}).noUnknown().strict().label("Push Subscription Schema");
|
|
4586
|
+
|
|
3205
4587
|
// src/user/user-completeness.schema.ts
|
|
3206
|
-
var
|
|
3207
|
-
var UserCompletenessSchema = (0,
|
|
4588
|
+
var import_yup55 = require("yup");
|
|
4589
|
+
var UserCompletenessSchema = (0, import_yup55.object)({
|
|
3208
4590
|
additionalInfo: CompletenessScoreSchema(0).label("Additional Info"),
|
|
3209
4591
|
// Optional
|
|
3210
4592
|
certifications: CompletenessScoreSchema(0).label("Certifications"),
|
|
@@ -3229,22 +4611,29 @@ var StudentCompletenessSchema = UserCompletenessSchema.omit([
|
|
|
3229
4611
|
0 && (module.exports = {
|
|
3230
4612
|
AnswerChoiceType,
|
|
3231
4613
|
ApplicationReceivePreference,
|
|
3232
|
-
|
|
3233
|
-
|
|
4614
|
+
BillingPeriod,
|
|
4615
|
+
BillingSubscriptionSchema,
|
|
4616
|
+
BillingTransactionSchema,
|
|
3234
4617
|
CAMPAIGN_MAX_RECIPIENTS_CEILING,
|
|
3235
|
-
|
|
4618
|
+
CONSUME_MAX_UNITS,
|
|
4619
|
+
CampaignDataSourceSchema,
|
|
3236
4620
|
CampaignEndKind,
|
|
3237
4621
|
CampaignIntervalUnit,
|
|
3238
4622
|
CampaignSchema,
|
|
3239
4623
|
CampaignStatus,
|
|
4624
|
+
CampaignStatusTransitions,
|
|
3240
4625
|
CampaignTriggerKind,
|
|
3241
4626
|
CampaignTriggerSchema,
|
|
3242
4627
|
ChoiceQuestionSchema,
|
|
4628
|
+
CityFilterSchema,
|
|
4629
|
+
CitySchema,
|
|
3243
4630
|
Common,
|
|
3244
4631
|
CompensationType,
|
|
3245
4632
|
CompletenessScoreSchema,
|
|
3246
4633
|
ContactTypes,
|
|
3247
4634
|
CoordinatorPageLinkSchema,
|
|
4635
|
+
CountryFilterSchema,
|
|
4636
|
+
CountrySchema,
|
|
3248
4637
|
DISPLAY_DATE_FORMAT,
|
|
3249
4638
|
DISPLAY_DATE_FORMAT_SHORT,
|
|
3250
4639
|
DateStringSchema,
|
|
@@ -3258,12 +4647,29 @@ var StudentCompletenessSchema = UserCompletenessSchema.omit([
|
|
|
3258
4647
|
EducationSchema,
|
|
3259
4648
|
EmployeeCount,
|
|
3260
4649
|
EmploymentType,
|
|
4650
|
+
EntitlementConsumeSchema,
|
|
4651
|
+
EntitlementDenialReason,
|
|
4652
|
+
EntitlementDenialSchema,
|
|
4653
|
+
EntitlingSubscriptionStatuses,
|
|
3261
4654
|
ExperienceLevel,
|
|
3262
4655
|
FEEDBACK_MESSAGE_MAX,
|
|
4656
|
+
FeatureEntitlementSchema,
|
|
4657
|
+
FeatureKey,
|
|
4658
|
+
FeedbackFilterSchema,
|
|
3263
4659
|
FeedbackSchema,
|
|
3264
4660
|
FeedbackStatus,
|
|
3265
4661
|
FeedbackType,
|
|
4662
|
+
GEO_COLLECTIONS,
|
|
4663
|
+
GEO_SEARCH_MAX,
|
|
4664
|
+
GRANT_MAX_LIMIT,
|
|
3266
4665
|
GeneraDetailFields,
|
|
4666
|
+
GeoKind,
|
|
4667
|
+
GeoSyncPhase,
|
|
4668
|
+
GeoSyncRunSchema,
|
|
4669
|
+
GeoSyncStatus,
|
|
4670
|
+
GeoSyncTrigger,
|
|
4671
|
+
GrantType,
|
|
4672
|
+
GroupFilterSchema,
|
|
3267
4673
|
GroupManagedBy,
|
|
3268
4674
|
GroupMembershipSchema,
|
|
3269
4675
|
GroupMembershipStatus,
|
|
@@ -3275,31 +4681,41 @@ var StudentCompletenessSchema = UserCompletenessSchema.omit([
|
|
|
3275
4681
|
JOB_TAXONOMY,
|
|
3276
4682
|
JobAlertFrequency,
|
|
3277
4683
|
JobCategory,
|
|
4684
|
+
JobFilterSchema,
|
|
3278
4685
|
JobIndustry,
|
|
3279
4686
|
JobSchema,
|
|
3280
4687
|
JobSearchUrgency,
|
|
3281
4688
|
JobStatus,
|
|
3282
4689
|
JobSubCategory,
|
|
3283
|
-
|
|
3284
|
-
ListFilterBranchSchema,
|
|
3285
|
-
ListFilterSchema,
|
|
4690
|
+
LanguageFilterSchema,
|
|
3286
4691
|
LocationSchema,
|
|
3287
4692
|
MIN_SALARY_LOWER_BOUND,
|
|
3288
4693
|
MIN_SALARY_UPPER_BOUND,
|
|
4694
|
+
MailingListFilterSchema,
|
|
3289
4695
|
MailingListSchema,
|
|
3290
|
-
|
|
4696
|
+
MoneySchema,
|
|
4697
|
+
NotificationType,
|
|
3291
4698
|
OccurrenceStatus,
|
|
3292
4699
|
PREFILL_MIRRORED_KEYS,
|
|
3293
4700
|
PRIVATE_PROFILE_FIELDS,
|
|
4701
|
+
PaddleEnvironment,
|
|
4702
|
+
PaddlePriceIdsSchema,
|
|
4703
|
+
PageFilterSchema,
|
|
3294
4704
|
PageSchema,
|
|
3295
4705
|
PageStatus,
|
|
3296
4706
|
PageType,
|
|
3297
4707
|
PaginationSchema,
|
|
4708
|
+
PlanGrantSchema,
|
|
4709
|
+
PlanKind,
|
|
4710
|
+
PlanSchema,
|
|
4711
|
+
PlanStatus,
|
|
3298
4712
|
PostSchema,
|
|
3299
4713
|
PostStatus,
|
|
3300
4714
|
ProficiencyLevel,
|
|
4715
|
+
PushSubscriptionSchema,
|
|
3301
4716
|
QuestionSchema,
|
|
3302
4717
|
QuestionType,
|
|
4718
|
+
QuotaPeriod,
|
|
3303
4719
|
ReadAndAcknowledgeQuestionSchema,
|
|
3304
4720
|
RecruiterPageLinkSchema,
|
|
3305
4721
|
ReferralSource,
|
|
@@ -3311,15 +4727,18 @@ var StudentCompletenessSchema = UserCompletenessSchema.omit([
|
|
|
3311
4727
|
SavedSearchSchema,
|
|
3312
4728
|
SavedSearchStatus,
|
|
3313
4729
|
SignupContextSource,
|
|
4730
|
+
SkillFilterSchema,
|
|
3314
4731
|
SkillSchema,
|
|
3315
4732
|
SocialAccount,
|
|
3316
4733
|
SocialAccountSchema,
|
|
4734
|
+
StateFilterSchema,
|
|
4735
|
+
StateSchema,
|
|
3317
4736
|
StudentCompletenessSchema,
|
|
3318
4737
|
StudyType,
|
|
4738
|
+
SubscriptionStatus,
|
|
3319
4739
|
SupportedAnswerChoiceTypes,
|
|
3320
4740
|
SupportedApplicationReceivePreferences,
|
|
3321
|
-
|
|
3322
|
-
SupportedCampaignAnchorFields,
|
|
4741
|
+
SupportedBillingPeriods,
|
|
3323
4742
|
SupportedCampaignEndKinds,
|
|
3324
4743
|
SupportedCampaignIntervalUnits,
|
|
3325
4744
|
SupportedCampaignStatuses,
|
|
@@ -3329,22 +4748,37 @@ var StudentCompletenessSchema = UserCompletenessSchema.omit([
|
|
|
3329
4748
|
SupportedEducationLevels,
|
|
3330
4749
|
SupportedEmployeeCounts,
|
|
3331
4750
|
SupportedEmploymentTypes,
|
|
4751
|
+
SupportedEntitlementDenialReasons,
|
|
3332
4752
|
SupportedExperienceLevels,
|
|
4753
|
+
SupportedFeatureKeys,
|
|
3333
4754
|
SupportedFeedbackStatuses,
|
|
3334
4755
|
SupportedFeedbackTypes,
|
|
4756
|
+
SupportedGeoKinds,
|
|
4757
|
+
SupportedGeoSyncPhases,
|
|
4758
|
+
SupportedGeoSyncStatuses,
|
|
4759
|
+
SupportedGeoSyncTriggers,
|
|
4760
|
+
SupportedGrantTypes,
|
|
4761
|
+
SupportedGroupManagedBy,
|
|
4762
|
+
SupportedGroupMembershipStatuses,
|
|
4763
|
+
SupportedGroupStatuses,
|
|
4764
|
+
SupportedGroupVisibilities,
|
|
3335
4765
|
SupportedJobAlertFrequencies,
|
|
3336
4766
|
SupportedJobCategories,
|
|
3337
4767
|
SupportedJobIndustries,
|
|
3338
4768
|
SupportedJobSearchUrgencies,
|
|
3339
4769
|
SupportedJobStatuses,
|
|
3340
4770
|
SupportedJobSubCategories,
|
|
3341
|
-
|
|
4771
|
+
SupportedNotificationTypes,
|
|
3342
4772
|
SupportedOccurrenceStatuses,
|
|
4773
|
+
SupportedPaddleEnvironments,
|
|
3343
4774
|
SupportedPageStatuses,
|
|
3344
4775
|
SupportedPageTypes,
|
|
4776
|
+
SupportedPlanKinds,
|
|
4777
|
+
SupportedPlanStatuses,
|
|
3345
4778
|
SupportedPostStatuses,
|
|
3346
4779
|
SupportedProficiencyLevels,
|
|
3347
4780
|
SupportedQuestionTypes,
|
|
4781
|
+
SupportedQuotaPeriods,
|
|
3348
4782
|
SupportedReferralSources,
|
|
3349
4783
|
SupportedReportReasons,
|
|
3350
4784
|
SupportedResourceTypes,
|
|
@@ -3353,17 +4787,26 @@ var StudentCompletenessSchema = UserCompletenessSchema.omit([
|
|
|
3353
4787
|
SupportedSignupContextSources,
|
|
3354
4788
|
SupportedSocialAccounts,
|
|
3355
4789
|
SupportedStudyTypes,
|
|
4790
|
+
SupportedSubscriptionStatuses,
|
|
4791
|
+
SupportedTemplateVariableBindingKinds,
|
|
4792
|
+
SupportedTransactionStatuses,
|
|
3356
4793
|
SupportedUserProfileVisibilities,
|
|
3357
4794
|
SupportedUserRoles,
|
|
3358
4795
|
SupportedUserStatuses,
|
|
3359
4796
|
SupportedWorkModes,
|
|
3360
4797
|
TAXONOMY_LABELS,
|
|
4798
|
+
TemplateVariableBindingKind,
|
|
4799
|
+
TemplateVariableBindingSchema,
|
|
4800
|
+
TerminalCampaignStatuses,
|
|
4801
|
+
TerminalGeoSyncStatuses,
|
|
3361
4802
|
TermsAcceptedSchema,
|
|
4803
|
+
TransactionStatus,
|
|
3362
4804
|
UserAdditionalInfoSchema,
|
|
3363
4805
|
UserCertificationSchema,
|
|
3364
4806
|
UserCompletenessSchema,
|
|
3365
4807
|
UserCoordinatorProfileSchema,
|
|
3366
4808
|
UserDetailType,
|
|
4809
|
+
UserFilterSchema,
|
|
3367
4810
|
UserGeneralDetailSchema,
|
|
3368
4811
|
UserIdAndCreatedAtSchema,
|
|
3369
4812
|
UserInterestSchema,
|
|
@@ -3378,12 +4821,24 @@ var StudentCompletenessSchema = UserCompletenessSchema.omit([
|
|
|
3378
4821
|
UserStatus,
|
|
3379
4822
|
WorkExperienceSchema,
|
|
3380
4823
|
WorkMode,
|
|
4824
|
+
canTransitionCampaign,
|
|
3381
4825
|
categoryOfIndustry,
|
|
3382
4826
|
categoryOfSubCategory,
|
|
4827
|
+
codeTokens,
|
|
4828
|
+
conjunction,
|
|
4829
|
+
currencyBound,
|
|
3383
4830
|
dateString,
|
|
4831
|
+
days,
|
|
3384
4832
|
deriveIndustry,
|
|
4833
|
+
enumFacet,
|
|
4834
|
+
epoch,
|
|
3385
4835
|
getSchemaByQuestion,
|
|
3386
4836
|
industriesOfCategory,
|
|
3387
4837
|
industryOfSubCategory,
|
|
3388
|
-
|
|
4838
|
+
months,
|
|
4839
|
+
percent,
|
|
4840
|
+
qualified,
|
|
4841
|
+
rejectUnknownKeys,
|
|
4842
|
+
subCategoriesOfIndustry,
|
|
4843
|
+
tokens
|
|
3389
4844
|
});
|