@thejob/schema 2.6.0 → 2.7.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/dist/index.cjs CHANGED
@@ -685,7 +685,7 @@ var GroupFilterSchema = rejectUnknownKeys(
685
685
  );
686
686
 
687
687
  // src/job/job.schema.ts
688
- var import_yup20 = require("yup");
688
+ var import_yup21 = require("yup");
689
689
 
690
690
  // src/location/location.schema.ts
691
691
  var import_yup7 = require("yup");
@@ -904,6 +904,338 @@ var SkillSchema = (0, import_yup15.object)({
904
904
  tags: (0, import_yup15.array)().of((0, import_yup15.string)().max(50)).max(20).optional().label("Tags")
905
905
  }).concat(DbDefaultSchema).noUnknown().strict().label("Skill");
906
906
 
907
+ // src/taxonomy/taxonomy.schema.ts
908
+ var import_yup16 = require("yup");
909
+
910
+ // src/taxonomy/taxonomy.constant.ts
911
+ var SupportedConceptSources = ["seed", "manual", "import"];
912
+ var SupportedTaxonomySources = ["seed", "manual"];
913
+ var SupportedTaxonomyNodeKinds = ["category", "subcategory", "group"];
914
+ var SupportedPrefilterRules = [
915
+ "spoken_language",
916
+ "no_letters",
917
+ "url_or_email",
918
+ "fragment"
919
+ ];
920
+ var SupportedUnmatchedResolutions = ["pending", "added", "rejected"];
921
+ var SupportedUnmatchedReasons = ["ambiguous"];
922
+ var SupportedProposalKinds = ["concept", "alias", "placement"];
923
+ var SupportedProposalFilters = [...SupportedProposalKinds, "uncategorized"];
924
+ var SupportedProposalStatuses = ["draft", "approved", "discarded"];
925
+ var SupportedProposalMessageRoles = ["operator", "model"];
926
+ var SupportedResolverVerdicts = [
927
+ "same",
928
+ "narrower",
929
+ "broader",
930
+ "related",
931
+ "new_concept",
932
+ "out_of_scope"
933
+ ];
934
+ var SupportedAuditStatuses = ["pending", "correct", "wrong"];
935
+ var SupportedReleaseKinds = ["seed", "manual", "review", "auto"];
936
+ var SupportedReleaseStatuses = ["open", "rolled_back"];
937
+ var SupportedMatchTypes = ["exact", "canonical", "fuzzy", "none"];
938
+ var SupportedTaxonomyJobKinds = [
939
+ "describe-concepts",
940
+ "retype-concepts",
941
+ "generate-aliases",
942
+ "alias-backfill",
943
+ "close-gaps",
944
+ "propose-placements",
945
+ "place-unplaced",
946
+ "calibrate-placement",
947
+ "reseed",
948
+ "reindex",
949
+ "calibrate-resolver",
950
+ "golden",
951
+ "resolve-backlog"
952
+ ];
953
+ var SupportedTaxonomyJobStates = ["running", "succeeded", "failed", "cancelled"];
954
+ var CONCEPT_ID_PATTERN = /^[a-z][a-z0-9-]*_[a-z0-9-]+$/;
955
+ var VOCABULARY_ID_PATTERN = /^[a-z][a-z0-9-]{0,31}$/;
956
+ var TAXONOMY_NODE_ID_PATTERN = /^[A-Za-z0-9_]{1,64}$/;
957
+ var CONCEPT_TYPE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
958
+ var SCHEDULE_MIN_HOURS = 1;
959
+ var SCHEDULE_MAX_HOURS = 24 * 30;
960
+
961
+ // src/taxonomy/taxonomy.schema.ts
962
+ var conceptId = () => (0, import_yup16.string)().matches(CONCEPT_ID_PATTERN, "conceptId must look like <vocabulary>_<slug>");
963
+ var vocabularyId = () => (0, import_yup16.string)().matches(VOCABULARY_ID_PATTERN, "vocabulary must match [a-z][a-z0-9-]{0,31}");
964
+ var nodeId = () => (0, import_yup16.string)().matches(TAXONOMY_NODE_ID_PATTERN, "node ids look like CAT_01_TECH");
965
+ var strings = () => (0, import_yup16.array)((0, import_yup16.string)().trim().min(1).required());
966
+ var VocabularyTypeSchema = (0, import_yup16.object)({
967
+ id: (0, import_yup16.string)().matches(CONCEPT_TYPE_PATTERN, "type ids match [a-z][a-z0-9_]*").required().label("Type ID"),
968
+ /** Shown to the model when it classifies a concept; include examples. */
969
+ description: (0, import_yup16.string)().defined().label("Description")
970
+ }).label("Vocabulary Type");
971
+ var VocabularySchema = (0, import_yup16.object)({
972
+ vocabularyId: vocabularyId().required().label("Vocabulary ID"),
973
+ name: (0, import_yup16.string)().trim().min(1).required().label("Name"),
974
+ /** What one concept is called in prompts and messages: "skill", "role". */
975
+ noun: (0, import_yup16.string)().trim().min(1).required().label("Noun"),
976
+ nounPlural: (0, import_yup16.string)().trim().min(1).required().label("Noun (plural)"),
977
+ /** What belongs in this vocabulary, for prompts. */
978
+ description: (0, import_yup16.string)().trim().min(1).required().label("Description"),
979
+ /** Where its terms are written, for the "swap it in" test: "a job posting". */
980
+ appearsIn: (0, import_yup16.string)().trim().min(1).required().label("Appears In"),
981
+ /** Things that are NOT concepts of this vocabulary, for prompts. */
982
+ outOfScope: (0, import_yup16.string)().trim().min(1).required().label("Out of Scope"),
983
+ types: (0, import_yup16.array)(VocabularyTypeSchema.required()).min(1).required().test(
984
+ "unique-type-ids",
985
+ "type ids must be unique",
986
+ (types) => !types || new Set(types.map((t) => t.id)).size === types.length
987
+ ).label("Types"),
988
+ /** The type a concept gets when nothing more specific is known. One of `types`. */
989
+ defaultType: (0, import_yup16.string)().required().test("default-type-listed", "defaultType must be one of the type ids", function(value) {
990
+ const types = this.parent.types ?? [];
991
+ return types.some((t) => t.id === value);
992
+ }).label("Default Type"),
993
+ prefilter: (0, import_yup16.array)((0, import_yup16.string)().oneOf(SupportedPrefilterRules).required()).default([]).label("Prefilter"),
994
+ /**
995
+ * Words that qualify a term without changing what it names ("fluent",
996
+ * "native"), dropped from its canonical lookup form in THIS vocabulary only.
997
+ */
998
+ qualifiers: strings().optional().label("Qualifiers"),
999
+ /** Extra instructions per model task; each is optional. */
1000
+ guidance: (0, import_yup16.object)({
1001
+ describe: (0, import_yup16.string)().optional(),
1002
+ classify: (0, import_yup16.string)().optional(),
1003
+ aliases: (0, import_yup16.string)().optional(),
1004
+ verify: (0, import_yup16.string)().optional(),
1005
+ place: (0, import_yup16.string)().optional()
1006
+ }).label("Guidance"),
1007
+ source: (0, import_yup16.string)().oneOf(SupportedTaxonomySources).required().label("Source"),
1008
+ createdAt: (0, import_yup16.number)().required().label("Created At"),
1009
+ updatedAt: (0, import_yup16.number)().required().label("Updated At")
1010
+ }).label("Vocabulary");
1011
+ var TaxonomyNodeSchema = (0, import_yup16.object)({
1012
+ nodeId: nodeId().required().label("Node ID"),
1013
+ vocabulary: vocabularyId().required().label("Vocabulary"),
1014
+ kind: (0, import_yup16.string)().oneOf(SupportedTaxonomyNodeKinds).required().label("Kind"),
1015
+ name: (0, import_yup16.string)().trim().min(1).required().label("Name"),
1016
+ /** Groups carry a description; categories and subcategories usually do not. */
1017
+ description: (0, import_yup16.string)().optional().label("Description"),
1018
+ aliases: strings().default([]).label("Aliases"),
1019
+ /** null for categories (the roots). */
1020
+ parentId: (0, import_yup16.string)().nullable().defined().label("Parent ID"),
1021
+ /** Ancestor ids from the root down to and including this node. */
1022
+ path: (0, import_yup16.array)((0, import_yup16.string)().required()).default([]).label("Path"),
1023
+ source: (0, import_yup16.string)().oneOf(SupportedTaxonomySources).optional().label("Source"),
1024
+ createdAt: (0, import_yup16.number)().required().label("Created At"),
1025
+ updatedAt: (0, import_yup16.number)().required().label("Updated At")
1026
+ }).label("Taxonomy Node");
1027
+ var ConceptMembershipSchema = (0, import_yup16.object)({
1028
+ categoryId: (0, import_yup16.string)().required().label("Category ID"),
1029
+ categoryName: (0, import_yup16.string)().required().label("Category Name"),
1030
+ subcategoryId: (0, import_yup16.string)().required().label("Subcategory ID"),
1031
+ subcategoryName: (0, import_yup16.string)().required().label("Subcategory Name"),
1032
+ groupId: (0, import_yup16.string)().required().label("Group ID"),
1033
+ groupName: (0, import_yup16.string)().required().label("Group Name"),
1034
+ /** The source record this membership came from; '' when written by hand. */
1035
+ sourceId: (0, import_yup16.string)().default("").label("Source ID")
1036
+ }).label("Concept Membership");
1037
+ var ConceptSchema = (0, import_yup16.object)({
1038
+ /** `<vocabulary>_<slug>`, e.g. `skills_power-bi`. Primary key. */
1039
+ conceptId: conceptId().required().label("Concept ID"),
1040
+ vocabulary: vocabularyId().required().label("Vocabulary"),
1041
+ name: (0, import_yup16.string)().trim().min(1).max(200).required().label("Name"),
1042
+ /** One of the vocabulary's type ids; checked against the vocabulary by the service. */
1043
+ type: (0, import_yup16.string)().trim().matches(CONCEPT_TYPE_PATTERN, "type must be a type id").required().label("Type"),
1044
+ description: (0, import_yup16.string)().trim().max(2e3).default("").label("Description"),
1045
+ aliases: strings().default([]).label("Aliases"),
1046
+ memberships: (0, import_yup16.array)(ConceptMembershipSchema.required()).default([]).label("Memberships"),
1047
+ /** Always concepts of the same vocabulary. */
1048
+ relatedConceptIds: strings().default([]).label("Related Concepts"),
1049
+ source: (0, import_yup16.string)().oneOf(SupportedConceptSources).required().label("Source"),
1050
+ createdAt: (0, import_yup16.number)().required().label("Created At"),
1051
+ updatedAt: (0, import_yup16.number)().required().label("Updated At"),
1052
+ /** Resume marker of `retype-concepts`: set once the type was examined. */
1053
+ retypedAt: (0, import_yup16.number)().optional().label("Retyped At"),
1054
+ /** Resume marker of `generate-aliases`: set even when no alias was added. */
1055
+ aliasedAt: (0, import_yup16.number)().optional().label("Aliased At"),
1056
+ /** Set while `description` is a stand-in; removed once a real one is written. */
1057
+ descriptionPending: (0, import_yup16.boolean)().isTrue().optional().label("Description Pending")
1058
+ }).label("Concept");
1059
+ var UnmatchedTermSchema = (0, import_yup16.object)({
1060
+ vocabulary: vocabularyId().required().label("Vocabulary"),
1061
+ /** The raw input as first received. */
1062
+ term: (0, import_yup16.string)().required().label("Term"),
1063
+ /** The dedupe key, so "React JS" and "react.js" share a row. */
1064
+ normalizedTerm: (0, import_yup16.string)().required().label("Normalized Term"),
1065
+ count: (0, import_yup16.number)().integer().min(0).required().label("Count"),
1066
+ firstSeenAt: (0, import_yup16.number)().required().label("First Seen At"),
1067
+ lastSeenAt: (0, import_yup16.number)().required().label("Last Seen At"),
1068
+ /** Which callers sent it, capped. */
1069
+ sources: (0, import_yup16.array)(
1070
+ (0, import_yup16.object)({
1071
+ service: (0, import_yup16.string)().required(),
1072
+ context: (0, import_yup16.string)().optional()
1073
+ }).required()
1074
+ ).default([]).label("Sources"),
1075
+ resolution: (0, import_yup16.string)().oneOf(SupportedUnmatchedResolutions).required().label("Resolution"),
1076
+ resolvedConceptId: (0, import_yup16.string)().optional().label("Resolved Concept"),
1077
+ /** `ambiguous`: the concept exists; the caller gave no context that picked one. */
1078
+ reason: (0, import_yup16.string)().oneOf(SupportedUnmatchedReasons).optional().label("Reason"),
1079
+ /** The concepts an ambiguous term could mean. */
1080
+ candidates: (0, import_yup16.array)((0, import_yup16.string)().required()).optional().label("Candidates"),
1081
+ /** Who rejected it when it was not a human: `prefilter:<rule>` or `model`. */
1082
+ rejectedBy: (0, import_yup16.string)().optional().label("Rejected By")
1083
+ }).label("Unmatched Term");
1084
+ var ProposalCandidateSchema = (0, import_yup16.object)({
1085
+ groupId: (0, import_yup16.string)().required(),
1086
+ groupName: (0, import_yup16.string)().required(),
1087
+ subcategoryId: (0, import_yup16.string)().required(),
1088
+ subcategoryName: (0, import_yup16.string)().required(),
1089
+ categoryId: (0, import_yup16.string)().required(),
1090
+ categoryName: (0, import_yup16.string)().required(),
1091
+ score: (0, import_yup16.number)().required(),
1092
+ nearestMembers: (0, import_yup16.array)((0, import_yup16.string)().required()).default([])
1093
+ }).label("Proposal Candidate");
1094
+ var ProposalMessageSchema = (0, import_yup16.object)({
1095
+ role: (0, import_yup16.string)().oneOf(SupportedProposalMessageRoles).required(),
1096
+ /** The operator's instruction, or the model's note on what it changed and why. */
1097
+ content: (0, import_yup16.string)().defined(),
1098
+ /** Which model answered. Absent on operator turns. */
1099
+ model: (0, import_yup16.string)().optional(),
1100
+ at: (0, import_yup16.number)().required()
1101
+ }).label("Proposal Message");
1102
+ var SuggestedHomeSchema = (0, import_yup16.object)({
1103
+ categoryName: (0, import_yup16.string)().defined(),
1104
+ subcategoryName: (0, import_yup16.string)().defined(),
1105
+ groupName: (0, import_yup16.string)().defined()
1106
+ }).label("Suggested Home");
1107
+ var ResolverVerificationSchema = (0, import_yup16.object)({
1108
+ verdict: (0, import_yup16.string)().oneOf(SupportedResolverVerdicts).required(),
1109
+ /** The shown concept the verdict is about; null for `new_concept` / `out_of_scope`. */
1110
+ conceptId: (0, import_yup16.string)().nullable().defined(),
1111
+ evidence: (0, import_yup16.string)().defined(),
1112
+ /** What the judge took the term to mean, abbreviations expanded. */
1113
+ means: (0, import_yup16.string)().optional(),
1114
+ /** For `same`: did the independent confirm step agree? */
1115
+ consistent: (0, import_yup16.boolean)().optional(),
1116
+ confirm: (0, import_yup16.object)({
1117
+ answer: (0, import_yup16.string)().oneOf(["yes", "no"]).required(),
1118
+ evidence: (0, import_yup16.string)().defined()
1119
+ }).optional().default(void 0)
1120
+ }).label("Resolver Verification");
1121
+ var ConceptProposalSchema = (0, import_yup16.object)({
1122
+ proposalId: (0, import_yup16.string)().required().label("Proposal ID"),
1123
+ vocabulary: vocabularyId().required().label("Vocabulary"),
1124
+ /** The miss this closes; `normalizedTerm` links back to its unmatched row. */
1125
+ term: (0, import_yup16.string)().required().label("Term"),
1126
+ normalizedTerm: (0, import_yup16.string)().required().label("Normalized Term"),
1127
+ /** Demand at proposal time, so the queue ranks without a join. */
1128
+ count: (0, import_yup16.number)().integer().min(0).required().label("Count"),
1129
+ /** Other misses folded into this draft because they name the same new concept. */
1130
+ mergedTerms: (0, import_yup16.array)(
1131
+ (0, import_yup16.object)({
1132
+ term: (0, import_yup16.string)().required(),
1133
+ normalizedTerm: (0, import_yup16.string)().required(),
1134
+ count: (0, import_yup16.number)().integer().min(0).required()
1135
+ }).required()
1136
+ ).optional().label("Merged Terms"),
1137
+ /** Absent on drafts that predate the field: read as `concept`. */
1138
+ kind: (0, import_yup16.string)().oneOf(SupportedProposalKinds).optional().label("Kind"),
1139
+ /** For `alias`: the concept the term names. For `placement`: the concept to place. */
1140
+ targetConceptId: (0, import_yup16.string)().optional().label("Target Concept"),
1141
+ targetName: (0, import_yup16.string)().optional().label("Target Name"),
1142
+ // The draft concept, editable until approved. Empty strings are valid here:
1143
+ // an alias draft carries no draft concept of its own.
1144
+ name: (0, import_yup16.string)().defined().label("Name"),
1145
+ type: (0, import_yup16.string)().defined().label("Type"),
1146
+ description: (0, import_yup16.string)().defined().label("Description"),
1147
+ aliases: (0, import_yup16.array)((0, import_yup16.string)().required()).default([]).label("Aliases"),
1148
+ /** Empty when no group fits: approved that way, the concept goes live uncategorized. */
1149
+ memberships: (0, import_yup16.array)(ConceptMembershipSchema.required()).default([]).label("Memberships"),
1150
+ suggestedHome: SuggestedHomeSchema.nullable().optional().default(void 0).label("Suggested Home"),
1151
+ /** Resolved to concepts that exist; names that matched nothing are dropped. */
1152
+ relatedConceptIds: (0, import_yup16.array)((0, import_yup16.string)().required()).default([]).label("Related Concepts"),
1153
+ /** Every placement candidate considered, best first. */
1154
+ candidates: (0, import_yup16.array)(ProposalCandidateSchema.required()).default([]).label("Candidates"),
1155
+ needsReview: (0, import_yup16.boolean)().required().label("Needs Review"),
1156
+ reason: (0, import_yup16.string)().optional().label("Reason"),
1157
+ verification: ResolverVerificationSchema.optional().default(void 0).label("Verification"),
1158
+ /** What the auto-accept gate decided for an alias verdict. */
1159
+ autoAccept: (0, import_yup16.object)({
1160
+ accepted: (0, import_yup16.boolean)().required(),
1161
+ reason: (0, import_yup16.string)().optional(),
1162
+ tier: (0, import_yup16.string)().optional(),
1163
+ calibrationId: (0, import_yup16.string)().optional(),
1164
+ releaseId: (0, import_yup16.string)().optional()
1165
+ }).optional().default(void 0).label("Auto Accept"),
1166
+ /** Which model drafted it. */
1167
+ model: (0, import_yup16.string)().defined().label("Model"),
1168
+ /** The refinement conversation, oldest first. */
1169
+ messages: (0, import_yup16.array)(ProposalMessageSchema.required()).default([]).label("Messages"),
1170
+ status: (0, import_yup16.string)().oneOf(SupportedProposalStatuses).required().label("Status"),
1171
+ /** Set when approved: the concept this became. */
1172
+ resolvedConceptId: (0, import_yup16.string)().optional().label("Resolved Concept"),
1173
+ createdAt: (0, import_yup16.number)().required().label("Created At"),
1174
+ updatedAt: (0, import_yup16.number)().required().label("Updated At")
1175
+ }).label("Concept Proposal");
1176
+ var ReleaseSchema = (0, import_yup16.object)({
1177
+ releaseId: (0, import_yup16.string)().required().label("Release ID"),
1178
+ kind: (0, import_yup16.string)().oneOf(SupportedReleaseKinds).required().label("Kind"),
1179
+ status: (0, import_yup16.string)().oneOf(SupportedReleaseStatuses).required().label("Status"),
1180
+ createdBy: (0, import_yup16.string)().required().label("Created By"),
1181
+ createdAt: (0, import_yup16.number)().required().label("Created At"),
1182
+ note: (0, import_yup16.string)().optional().label("Note"),
1183
+ rolledBackAt: (0, import_yup16.number)().optional().label("Rolled Back At"),
1184
+ rolledBackBy: (0, import_yup16.string)().optional().label("Rolled Back By")
1185
+ }).label("Release");
1186
+ var AuditSchema = (0, import_yup16.object)({
1187
+ auditId: (0, import_yup16.string)().required().label("Audit ID"),
1188
+ releaseId: (0, import_yup16.string)().required().label("Release ID"),
1189
+ key: (0, import_yup16.string)().required().label("Key"),
1190
+ term: (0, import_yup16.string)().required().label("Term"),
1191
+ conceptId: (0, import_yup16.string)().required().label("Concept ID"),
1192
+ tier: (0, import_yup16.string)().optional().label("Tier"),
1193
+ status: (0, import_yup16.string)().oneOf(SupportedAuditStatuses).required().label("Status"),
1194
+ createdAt: (0, import_yup16.number)().required().label("Created At"),
1195
+ resolvedAt: (0, import_yup16.number)().optional().label("Resolved At"),
1196
+ resolvedBy: (0, import_yup16.string)().optional().label("Resolved By")
1197
+ }).label("Audit");
1198
+ var ResolverSettingsSchema = (0, import_yup16.object)({
1199
+ autoAccept: (0, import_yup16.object)({
1200
+ /** Stop switch for WRITING auto keys. Off until a calibration passes. */
1201
+ enabled: (0, import_yup16.boolean)().required(),
1202
+ /** Minimum calibrated precision (Wilson 95% lower bound) for a tier to auto-accept. */
1203
+ target: (0, import_yup16.number)().min(0.5).lessThan(1).required(),
1204
+ maxPerRun: (0, import_yup16.number)().integer().min(0).required(),
1205
+ maxPerDay: (0, import_yup16.number)().integer().min(0).required(),
1206
+ /** Share of each auto release sampled into the audit queue (0..1). */
1207
+ auditRate: (0, import_yup16.number)().min(0).max(1).required()
1208
+ }).label("Auto Accept"),
1209
+ /** Stop switch for SERVING auto keys: false hides them from lookups at once. */
1210
+ serveAutoKeys: (0, import_yup16.boolean)().required().label("Serve Auto Keys"),
1211
+ /** Minimum Meilisearch ranking score (0..1) for a fuzzy hit to be considered. */
1212
+ fuzzyMinScore: (0, import_yup16.number)().min(0).max(1).required().label("Fuzzy Min Score"),
1213
+ /** Per-client limit on public search, per minute per replica. 0 disables it. */
1214
+ searchRateLimitPerMin: (0, import_yup16.number)().integer().min(0).required().label("Search Rate Limit"),
1215
+ /** Master switch for scheduled maintenance jobs. */
1216
+ scheduledJobs: (0, import_yup16.boolean)().required().label("Scheduled Jobs"),
1217
+ updatedAt: (0, import_yup16.number)().optional().label("Updated At"),
1218
+ updatedBy: (0, import_yup16.string)().optional().label("Updated By"),
1219
+ /** Why auto-accept was last switched off automatically. */
1220
+ trippedReason: (0, import_yup16.string)().optional().label("Tripped Reason")
1221
+ }).label("Resolver Settings");
1222
+ var TaxonomyScheduleSchema = (0, import_yup16.object)({
1223
+ kind: (0, import_yup16.string)().oneOf(SupportedTaxonomyJobKinds).required().label("Job"),
1224
+ enabled: (0, import_yup16.boolean)().required().label("Enabled"),
1225
+ everyHours: (0, import_yup16.number)().min(SCHEDULE_MIN_HOURS).max(SCHEDULE_MAX_HOURS).required().label("Every (hours)"),
1226
+ /** Last time the scheduler fired this job. */
1227
+ lastRunAt: (0, import_yup16.number)().optional().label("Last Run At"),
1228
+ updatedAt: (0, import_yup16.number)().required().label("Updated At")
1229
+ }).label("Taxonomy Schedule");
1230
+ var NormalizeContextSchema = (0, import_yup16.object)({
1231
+ /** The title of the record the terms come from. */
1232
+ title: (0, import_yup16.string)().max(200).optional(),
1233
+ /** Backbone node ids (category, subcategory or group). */
1234
+ categoryIds: (0, import_yup16.array)(nodeId().required()).max(10).optional(),
1235
+ /** Other terms from the same record, in the same vocabulary. */
1236
+ siblings: (0, import_yup16.array)((0, import_yup16.string)().max(200).required()).max(50).optional()
1237
+ }).label("Normalize Context");
1238
+
907
1239
  // src/question/question.constant.ts
908
1240
  var QuestionType = /* @__PURE__ */ ((QuestionType2) => {
909
1241
  QuestionType2["Input"] = "input";
@@ -920,24 +1252,24 @@ var AnswerChoiceType = /* @__PURE__ */ ((AnswerChoiceType2) => {
920
1252
  var SupportedAnswerChoiceTypes = Object.values(AnswerChoiceType);
921
1253
 
922
1254
  // src/question/question.schema.ts
923
- var import_yup16 = require("yup");
924
- var QuestionSchema = (0, import_yup16.object)().shape({
925
- label: (0, import_yup16.string)().max(50).optional().label("Label"),
926
- default: (0, import_yup16.boolean)().optional().default(false).label("Default"),
927
- title: (0, import_yup16.string)().required().max(200).label("Title"),
928
- type: (0, import_yup16.string)().oneOf(SupportedQuestionTypes).required().label("Type"),
929
- isOptional: (0, import_yup16.boolean)().optional().label("Mark if the question is optional"),
930
- readonly: (0, import_yup16.boolean)().optional().label("Readonly"),
931
- isNew: (0, import_yup16.boolean)().optional().label("Is New")
1255
+ var import_yup17 = require("yup");
1256
+ var QuestionSchema = (0, import_yup17.object)().shape({
1257
+ label: (0, import_yup17.string)().max(50).optional().label("Label"),
1258
+ default: (0, import_yup17.boolean)().optional().default(false).label("Default"),
1259
+ title: (0, import_yup17.string)().required().max(200).label("Title"),
1260
+ type: (0, import_yup17.string)().oneOf(SupportedQuestionTypes).required().label("Type"),
1261
+ isOptional: (0, import_yup17.boolean)().optional().label("Mark if the question is optional"),
1262
+ readonly: (0, import_yup17.boolean)().optional().label("Readonly"),
1263
+ isNew: (0, import_yup17.boolean)().optional().label("Is New")
932
1264
  });
933
1265
 
934
1266
  // src/question/input-question.schema.ts
935
- var import_yup17 = require("yup");
1267
+ var import_yup18 = require("yup");
936
1268
  var InputQuestionSchema = QuestionSchema.concat(
937
- (0, import_yup17.object)().shape({
938
- type: (0, import_yup17.string)().oneOf(["input" /* Input */]).required().label("Question type"),
939
- preferredAnswer: (0, import_yup17.string)().max(100).optional().label("Preferred Answer"),
940
- answers: (0, import_yup17.string)().max(500).when("isOptional", {
1269
+ (0, import_yup18.object)().shape({
1270
+ type: (0, import_yup18.string)().oneOf(["input" /* Input */]).required().label("Question type"),
1271
+ preferredAnswer: (0, import_yup18.string)().max(100).optional().label("Preferred Answer"),
1272
+ answers: (0, import_yup18.string)().max(500).when("isOptional", {
941
1273
  is: (isOptional) => Boolean(isOptional),
942
1274
  then: (schema) => schema.optional(),
943
1275
  otherwise: (schema) => schema.required()
@@ -946,10 +1278,10 @@ var InputQuestionSchema = QuestionSchema.concat(
946
1278
  );
947
1279
 
948
1280
  // src/question/choice-question.schema.ts
949
- var import_yup18 = require("yup");
950
- var ChoicePreferredAnswerSchema = (label = "Preferred answer") => (0, import_yup18.mixed)().when("answerChoiceType", {
1281
+ var import_yup19 = require("yup");
1282
+ var ChoicePreferredAnswerSchema = (label = "Preferred answer") => (0, import_yup19.mixed)().when("answerChoiceType", {
951
1283
  is: (value) => value === "multiple" /* Multiple */,
952
- then: () => (0, import_yup18.array)((0, import_yup18.string)()).test(
1284
+ then: () => (0, import_yup19.array)((0, import_yup19.string)()).test(
953
1285
  "is-valid-option",
954
1286
  `${label} must be from the provided options`,
955
1287
  function(answer) {
@@ -960,7 +1292,7 @@ var ChoicePreferredAnswerSchema = (label = "Preferred answer") => (0, import_yup
960
1292
  return true;
961
1293
  }
962
1294
  ).optional().label(label),
963
- otherwise: () => (0, import_yup18.string)().test(
1295
+ otherwise: () => (0, import_yup19.string)().test(
964
1296
  "is-valid-option",
965
1297
  `${label} must be from the provided options`,
966
1298
  function(answer) {
@@ -971,20 +1303,20 @@ var ChoicePreferredAnswerSchema = (label = "Preferred answer") => (0, import_yup
971
1303
  });
972
1304
  var getChoiceAnswerSchema = (answerChoiceType, isOptional) => {
973
1305
  if (answerChoiceType === "multiple" /* Multiple */) {
974
- const multiAnswerSchema = (0, import_yup18.array)((0, import_yup18.string)().max(500));
1306
+ const multiAnswerSchema = (0, import_yup19.array)((0, import_yup19.string)().max(500));
975
1307
  return isOptional ? multiAnswerSchema.optional().default([]) : multiAnswerSchema.min(1, "Select at least one option.").required("Select at least one option.").default([]);
976
1308
  }
977
- const singleAnswerSchema = (0, import_yup18.string)().max(500);
1309
+ const singleAnswerSchema = (0, import_yup19.string)().max(500);
978
1310
  return isOptional ? singleAnswerSchema.optional() : singleAnswerSchema.required("Select an option.");
979
1311
  };
980
1312
  var ChoiceQuestionSchema = QuestionSchema.concat(
981
- (0, import_yup18.object)().shape({
982
- type: (0, import_yup18.string)().oneOf(["choice" /* Choice */]).required().label("Question type"),
983
- optionsFrom: (0, import_yup18.string)().oneOf(["skill" /* Skill */, "education_level" /* EducationLevel */, "experience_level" /* ExperienceLevel */]).optional().label("Option field"),
984
- options: (0, import_yup18.array)((0, import_yup18.string)().required()).min(1).required().label("Options"),
985
- answerChoiceType: (0, import_yup18.string)().oneOf(SupportedAnswerChoiceTypes).required().default("single" /* Single */).label("Type"),
1313
+ (0, import_yup19.object)().shape({
1314
+ type: (0, import_yup19.string)().oneOf(["choice" /* Choice */]).required().label("Question type"),
1315
+ optionsFrom: (0, import_yup19.string)().oneOf(["skill" /* Skill */, "education_level" /* EducationLevel */, "experience_level" /* ExperienceLevel */]).optional().label("Option field"),
1316
+ options: (0, import_yup19.array)((0, import_yup19.string)().required()).min(1).required().label("Options"),
1317
+ answerChoiceType: (0, import_yup19.string)().oneOf(SupportedAnswerChoiceTypes).required().default("single" /* Single */).label("Type"),
986
1318
  preferredAnswer: ChoicePreferredAnswerSchema("Preferred answer"),
987
- answers: (0, import_yup18.mixed)().when(
1319
+ answers: (0, import_yup19.mixed)().when(
988
1320
  ["answerChoiceType", "isOptional"],
989
1321
  ([answerChoiceType, isOptional]) => getChoiceAnswerSchema(answerChoiceType, isOptional)
990
1322
  )
@@ -992,12 +1324,12 @@ var ChoiceQuestionSchema = QuestionSchema.concat(
992
1324
  );
993
1325
 
994
1326
  // src/question/read-and-acknowledge.schema.ts
995
- var import_yup19 = require("yup");
996
- var AcknowledgeAnswerSchema = (0, import_yup19.string)().oneOf(["yes" /* Yes */, "no" /* No */]).required("This question must be acknowledged.");
1327
+ var import_yup20 = require("yup");
1328
+ var AcknowledgeAnswerSchema = (0, import_yup20.string)().oneOf(["yes" /* Yes */, "no" /* No */]).required("This question must be acknowledged.");
997
1329
  var ReadAndAcknowledgeQuestionSchema = QuestionSchema.concat(
998
- (0, import_yup19.object)().shape({
999
- type: (0, import_yup19.string)().oneOf(["readAndAcknowledge" /* ReadAndAcknowledge */]).required().label("Question type"),
1000
- description: (0, import_yup19.string)().max(1e4).required().label("Content"),
1330
+ (0, import_yup20.object)().shape({
1331
+ type: (0, import_yup20.string)().oneOf(["readAndAcknowledge" /* ReadAndAcknowledge */]).required().label("Question type"),
1332
+ description: (0, import_yup20.string)().max(1e4).required().label("Content"),
1001
1333
  preferredAnswer: AcknowledgeAnswerSchema.label("Preferred Answer"),
1002
1334
  answers: AcknowledgeAnswerSchema.label("Answer")
1003
1335
  })
@@ -1078,20 +1410,20 @@ var SupportedApplicationReceivePreferences = Object.values(
1078
1410
  );
1079
1411
 
1080
1412
  // src/job/job.schema.ts
1081
- var JobSchema = (0, import_yup20.object)({
1413
+ var JobSchema = (0, import_yup21.object)({
1082
1414
  /** Unique id usable by external systems to reference the job. */
1083
- uniqueId: (0, import_yup20.string)().optional().label("Unique ID"),
1415
+ uniqueId: (0, import_yup21.string)().optional().label("Unique ID"),
1084
1416
  /** Job title shown in listings and search. */
1085
- headline: (0, import_yup20.string)().required().min(5).max(150).label("Headline"),
1417
+ headline: (0, import_yup21.string)().required().min(5).max(150).label("Headline"),
1086
1418
  /** User-friendly, unique URL identifier. */
1087
- slug: (0, import_yup20.string)().required().max(250).label("Slug"),
1419
+ slug: (0, import_yup21.string)().required().max(250).label("Slug"),
1088
1420
  /** How applications are received (inbox vs external website). */
1089
- applicationReceivePreference: (0, import_yup20.mixed)().oneOf([
1421
+ applicationReceivePreference: (0, import_yup21.mixed)().oneOf([
1090
1422
  "inbox" /* Inbox */,
1091
1423
  "external_website" /* ExternalWebsite */
1092
1424
  ]).required().label("Preference"),
1093
1425
  /** Required when applicationReceivePreference is ExternalWebsite. */
1094
- externalApplyLink: (0, import_yup20.string)().when("applicationReceivePreference", {
1426
+ externalApplyLink: (0, import_yup21.string)().when("applicationReceivePreference", {
1095
1427
  is: (value) => value === "external_website" /* ExternalWebsite */,
1096
1428
  then: (schema) => schema.url().required().label("Apply for Job link"),
1097
1429
  otherwise: (schema) => schema.optional()
@@ -1099,35 +1431,44 @@ var JobSchema = (0, import_yup20.object)({
1099
1431
  /** Company the job belongs to (subset of PageSchema). */
1100
1432
  company: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Company"),
1101
1433
  /** @deprecated kept for backwards compatibility. */
1102
- designation: (0, import_yup20.string)().optional().nullable().label("Designation"),
1103
- employmentType: (0, import_yup20.string)().required().label("Employment type"),
1104
- workMode: (0, import_yup20.string)().required().label("Work mode"),
1434
+ designation: (0, import_yup21.string)().optional().nullable().label("Designation"),
1435
+ employmentType: (0, import_yup21.string)().required().label("Employment type"),
1436
+ workMode: (0, import_yup21.string)().required().label("Work mode"),
1105
1437
  /**
1106
- * `conceptId`/`conceptName` carry the taxonomy-service normalization. They are
1107
- * picked here (not just `name`/`logo`) because SkillSchema is `.noUnknown()`:
1108
- * an unpicked field that is actually PRESENT fails validation, so omitting them
1109
- * would reject every normalized job.
1438
+ * `name` is the skill exactly as posted. `concept` is what thejob-taxonomy-service
1439
+ * resolved it to (`vocabulary: skills`), written by jobs-service, never by the
1440
+ * poster: enough of the concept to filter by id and display the canonical name
1441
+ * without a lookup. A copy, so it can go stale after a taxonomy release; see
1442
+ * `taxonomyVersion`.
1443
+ *
1444
+ * Additive, never authoritative: the raw `name` is always kept, so a wrong
1445
+ * match, a taxonomy release or an outage is recoverable by re-normalizing.
1446
+ * `concept` is absent when the term did not resolve (a normal outcome, logged
1447
+ * as a miss on the taxonomy side) or the job predates normalization, so read
1448
+ * `concept?.name ?? name`.
1110
1449
  */
1111
- skills: (0, import_yup20.array)().of(
1112
- SkillSchema.pick(["name", "logo", "conceptId", "conceptName"]).required().label("Skill")
1450
+ skills: (0, import_yup21.array)().of(
1451
+ SkillSchema.pick(["name", "logo"]).shape({
1452
+ concept: ConceptSchema.pick(["conceptId", "vocabulary", "name", "type"]).nullable().optional().default(void 0)
1453
+ }).required().label("Skill")
1113
1454
  ).nullable().optional().label("Skills"),
1114
- bookmarks: (0, import_yup20.array)().of(UserIdAndCreatedAtSchema).optional().label("Bookmarks"),
1115
- embedding: (0, import_yup20.object)({
1116
- vector: (0, import_yup20.array)((0, import_yup20.number)().required()).optional().label("Embedding vector"),
1117
- model: (0, import_yup20.string)().optional().label("Embedding model")
1455
+ bookmarks: (0, import_yup21.array)().of(UserIdAndCreatedAtSchema).optional().label("Bookmarks"),
1456
+ embedding: (0, import_yup21.object)({
1457
+ vector: (0, import_yup21.array)((0, import_yup21.number)().required()).optional().label("Embedding vector"),
1458
+ model: (0, import_yup21.string)().optional().label("Embedding model")
1118
1459
  }).nullable().optional().label("Embedding"),
1119
- applicants: (0, import_yup20.array)().of(UserIdAndCreatedAtSchema).optional().label("Applicants"),
1120
- applicantCount: (0, import_yup20.number)().optional().label("Applicant count"),
1121
- externalApplicationLinkClickCount: (0, import_yup20.number)().optional().label("External application link click count"),
1122
- description: (0, import_yup20.string)().required().min(100).max(5e4).label("Description"),
1123
- descriptionMarkdown: (0, import_yup20.string)().required().label("Description Markdown"),
1124
- descriptionHTML: (0, import_yup20.string)().optional().label("Description HTML"),
1460
+ applicants: (0, import_yup21.array)().of(UserIdAndCreatedAtSchema).optional().label("Applicants"),
1461
+ applicantCount: (0, import_yup21.number)().optional().label("Applicant count"),
1462
+ externalApplicationLinkClickCount: (0, import_yup21.number)().optional().label("External application link click count"),
1463
+ description: (0, import_yup21.string)().required().min(100).max(5e4).label("Description"),
1464
+ descriptionMarkdown: (0, import_yup21.string)().required().label("Description Markdown"),
1465
+ descriptionHTML: (0, import_yup21.string)().optional().label("Description HTML"),
1125
1466
  /**
1126
1467
  * Job locations. Required even for remote roles (compliance/legal). Defaults
1127
1468
  * to an empty array; min(1) is intentionally not enforced yet.
1128
1469
  */
1129
- locations: (0, import_yup20.array)().of(LocationSchema).default([]).label("Location"),
1130
- educationLevel: (0, import_yup20.array)((0, import_yup20.string)().oneOf(SupportedEducationLevels)).required().min(1).label("Educational level"),
1470
+ locations: (0, import_yup21.array)().of(LocationSchema).default([]).label("Location"),
1471
+ educationLevel: (0, import_yup21.array)((0, import_yup21.string)().oneOf(SupportedEducationLevels)).required().min(1).label("Educational level"),
1131
1472
  /** Posting validity window (start/expiry). */
1132
1473
  validity: DurationSchema({
1133
1474
  format: SYSTEM_DATE_FORMAT,
@@ -1140,22 +1481,22 @@ var JobSchema = (0, import_yup20.object)({
1140
1481
  * has no `validity.endDate` and must be verified by crawling its page. Guards
1141
1482
  * re-check cadence so the same job is not re-crawled on every sweep.
1142
1483
  */
1143
- lastExpiryCheckAt: (0, import_yup20.number)().optional().nullable().label("Last expiry check at"),
1484
+ lastExpiryCheckAt: (0, import_yup21.number)().optional().nullable().label("Last expiry check at"),
1144
1485
  /**
1145
1486
  * When this job transitioned to `status: closed` (epoch ms). Server-owned
1146
1487
  * operational field: stamped by every jobs-service close path (expiry
1147
1488
  * auto-close and the dated lapse sweep). Distinct from `updatedAt` so a later
1148
1489
  * edit to a closed job does not move it.
1149
1490
  */
1150
- closedAt: (0, import_yup20.number)().optional().nullable().label("Closed at"),
1151
- experienceLevel: (0, import_yup20.string)().oneOf(SupportedExperienceLevels).nullable().optional().label("Experience level"),
1152
- contactEmail: (0, import_yup20.string)().email().nullable().optional().label("Contact email"),
1153
- workingHoursPerWeek: (0, import_yup20.number)().nullable().optional().min(1).max(24 * 7).label("Working hours per week"),
1154
- tags: (0, import_yup20.array)().max(50).optional().label("Tags"),
1491
+ closedAt: (0, import_yup21.number)().optional().nullable().label("Closed at"),
1492
+ experienceLevel: (0, import_yup21.string)().oneOf(SupportedExperienceLevels).nullable().optional().label("Experience level"),
1493
+ contactEmail: (0, import_yup21.string)().email().nullable().optional().label("Contact email"),
1494
+ workingHoursPerWeek: (0, import_yup21.number)().nullable().optional().min(1).max(24 * 7).label("Working hours per week"),
1495
+ tags: (0, import_yup21.array)().max(50).optional().label("Tags"),
1155
1496
  /** Direct/Quick-apply questionnaire. Each item is validated against the
1156
1497
  * concrete question schema for its `type`, with the stored `answers` omitted. */
1157
- questionnaire: (0, import_yup20.array)().of(
1158
- (0, import_yup20.lazy)((question) => {
1498
+ questionnaire: (0, import_yup21.array)().of(
1499
+ (0, import_yup21.lazy)((question) => {
1159
1500
  const schema = getSchemaByQuestion(question.type);
1160
1501
  if (question.type === "input" /* Input */) {
1161
1502
  return schema.omit([
@@ -1171,12 +1512,12 @@ var JobSchema = (0, import_yup20.object)({
1171
1512
  return schema;
1172
1513
  })
1173
1514
  ).label("Questionnaire"),
1174
- status: (0, import_yup20.string)().oneOf(SupportedJobStatuses).default("draft" /* Draft */).required().label("Status"),
1175
- seoTags: (0, import_yup20.object)({
1176
- description: (0, import_yup20.string)().optional().label("Description"),
1177
- keywords: (0, import_yup20.array)().of((0, import_yup20.string)()).optional().label("Keywords")
1515
+ status: (0, import_yup21.string)().oneOf(SupportedJobStatuses).default("draft" /* Draft */).required().label("Status"),
1516
+ seoTags: (0, import_yup21.object)({
1517
+ description: (0, import_yup21.string)().optional().label("Description"),
1518
+ keywords: (0, import_yup21.array)().of((0, import_yup21.string)()).optional().label("Keywords")
1178
1519
  }).nullable().optional().label("SEO Tags"),
1179
- jdSummary: (0, import_yup20.string)().optional().label("JD Summary"),
1520
+ jdSummary: (0, import_yup21.string)().optional().label("JD Summary"),
1180
1521
  /**
1181
1522
  * Structured, model-extracted sections of the posting, re-organized into clean
1182
1523
  * bullet points. This is our OWN unique presentation of the job (rendered as the
@@ -1185,28 +1526,28 @@ var JobSchema = (0, import_yup20.object)({
1185
1526
  * optional: absent on jobs not yet (re-)enriched, and any section is `[]`/omitted
1186
1527
  * when the posting genuinely has none.
1187
1528
  */
1188
- jobHighlights: (0, import_yup20.object)({
1189
- responsibilities: (0, import_yup20.array)().of((0, import_yup20.string)()).optional().label("Responsibilities"),
1190
- requirements: (0, import_yup20.array)().of((0, import_yup20.string)()).optional().label("Requirements"),
1191
- benefits: (0, import_yup20.array)().of((0, import_yup20.string)()).optional().label("Benefits"),
1192
- qualifications: (0, import_yup20.array)().of((0, import_yup20.string)()).optional().label("Qualifications")
1529
+ jobHighlights: (0, import_yup21.object)({
1530
+ responsibilities: (0, import_yup21.array)().of((0, import_yup21.string)()).optional().label("Responsibilities"),
1531
+ requirements: (0, import_yup21.array)().of((0, import_yup21.string)()).optional().label("Requirements"),
1532
+ benefits: (0, import_yup21.array)().of((0, import_yup21.string)()).optional().label("Benefits"),
1533
+ qualifications: (0, import_yup21.array)().of((0, import_yup21.string)()).optional().label("Qualifications")
1193
1534
  }).nullable().optional().label("Job Highlights"),
1194
- canCreateAlert: (0, import_yup20.boolean)().optional().label("Can create alert"),
1195
- canDirectApply: (0, import_yup20.boolean)().optional().label("Can direct apply"),
1196
- hasApplied: (0, import_yup20.boolean)().optional().label("Has applied"),
1197
- isOwner: (0, import_yup20.boolean)().optional().label("Is owner"),
1198
- hasBookmarked: (0, import_yup20.boolean)().optional().label("Has bookmarked"),
1199
- hasReported: (0, import_yup20.boolean)().optional().label("Has reported"),
1200
- ratePerHour: (0, import_yup20.number)().optional().min(0).label("Rate per hour"),
1201
- isPromoted: (0, import_yup20.boolean)().optional().label("Is promoted"),
1202
- salaryRange: (0, import_yup20.object)({
1203
- currency: (0, import_yup20.string)().required().label("Currency").default("USD"),
1204
- min: (0, import_yup20.number)().nullable().optional().min(0).label("Minimum Salary"),
1205
- max: (0, import_yup20.number)().nullable().optional().min(0).label("Maximum Salary"),
1206
- compensationType: (0, import_yup20.string)().oneOf(SupportedCompensationTypes).nullable().optional().label("Compensation Type"),
1207
- isNegotiable: (0, import_yup20.boolean)().nullable().optional().default(true).label("Is Negotiable")
1535
+ canCreateAlert: (0, import_yup21.boolean)().optional().label("Can create alert"),
1536
+ canDirectApply: (0, import_yup21.boolean)().optional().label("Can direct apply"),
1537
+ hasApplied: (0, import_yup21.boolean)().optional().label("Has applied"),
1538
+ isOwner: (0, import_yup21.boolean)().optional().label("Is owner"),
1539
+ hasBookmarked: (0, import_yup21.boolean)().optional().label("Has bookmarked"),
1540
+ hasReported: (0, import_yup21.boolean)().optional().label("Has reported"),
1541
+ ratePerHour: (0, import_yup21.number)().optional().min(0).label("Rate per hour"),
1542
+ isPromoted: (0, import_yup21.boolean)().optional().label("Is promoted"),
1543
+ salaryRange: (0, import_yup21.object)({
1544
+ currency: (0, import_yup21.string)().required().label("Currency").default("USD"),
1545
+ min: (0, import_yup21.number)().nullable().optional().min(0).label("Minimum Salary"),
1546
+ max: (0, import_yup21.number)().nullable().optional().min(0).label("Maximum Salary"),
1547
+ compensationType: (0, import_yup21.string)().oneOf(SupportedCompensationTypes).nullable().optional().label("Compensation Type"),
1548
+ isNegotiable: (0, import_yup21.boolean)().nullable().optional().default(true).label("Is Negotiable")
1208
1549
  }).nullable().optional().label("Salary Range"),
1209
- reports: (0, import_yup20.array)().of(UserIdAndCreatedAtSchema).optional().label("Reports"),
1550
+ reports: (0, import_yup21.array)().of(UserIdAndCreatedAtSchema).optional().label("Reports"),
1210
1551
  // Taxonomy fields are constants (see job-taxonomy.constant.ts), stored as slugs and
1211
1552
  // translated for display. They are deliberately NOT `oneOf`-constrained here: a value
1212
1553
  // outside the taxonomy must be DROPPED, never a reason to reject the posting. Yup
@@ -1219,17 +1560,25 @@ var JobSchema = (0, import_yup20.object)({
1219
1560
  // time (values get renamed, retired, added), so stored jobs will always outlive the
1220
1561
  // enum — being lenient here is what lets the taxonomy evolve without a migration
1221
1562
  // gate on every write.
1222
- category: (0, import_yup20.string)().nullable().optional().label("Category"),
1223
- industry: (0, import_yup20.string)().nullable().optional().label("Industry"),
1224
- subCategories: (0, import_yup20.array)().of((0, import_yup20.string)().trim().required()).nullable().optional().label("Sub Categories"),
1225
- termsAccepted: (0, import_yup20.boolean)().oneOf([true], "Accept terms before proceeding").required("Accept terms before proceeding").label("Terms accepted"),
1226
- isFeatured: (0, import_yup20.boolean)().optional().default(false).label("Is featured"),
1563
+ category: (0, import_yup21.string)().nullable().optional().label("Category"),
1564
+ industry: (0, import_yup21.string)().nullable().optional().label("Industry"),
1565
+ subCategories: (0, import_yup21.array)().of((0, import_yup21.string)().trim().required()).nullable().optional().label("Sub Categories"),
1566
+ /**
1567
+ * The taxonomy state `skills[].concept` was resolved against (the service's
1568
+ * `taxonomyVersion`, bumped by every release and rollback); behind the current
1569
+ * version means the concepts may be stale. Written by jobs-service with every
1570
+ * skills write: `null` when that write could not be normalized (taxonomy down
1571
+ * or unconfigured), absent on jobs that predate normalization.
1572
+ */
1573
+ taxonomyVersion: (0, import_yup21.number)().integer().min(0).nullable().optional().label("Taxonomy Version"),
1574
+ termsAccepted: (0, import_yup21.boolean)().oneOf([true], "Accept terms before proceeding").required("Accept terms before proceeding").label("Terms accepted"),
1575
+ isFeatured: (0, import_yup21.boolean)().optional().default(false).label("Is featured"),
1227
1576
  /** Market country codes to feature this job in; empty = global. */
1228
- featuredMarkets: (0, import_yup20.array)().of((0, import_yup20.string)().required()).optional().default([]).label("Featured markets")
1577
+ featuredMarkets: (0, import_yup21.array)().of((0, import_yup21.string)().required()).optional().default([]).label("Featured markets")
1229
1578
  }).concat(DbDefaultSchema).noUnknown().label("Job");
1230
1579
 
1231
1580
  // src/job/job-filter.schema.ts
1232
- var import_yup21 = require("yup");
1581
+ var import_yup22 = require("yup");
1233
1582
 
1234
1583
  // src/user/user.constant.ts
1235
1584
  var UserRole = /* @__PURE__ */ ((UserRole2) => {
@@ -2417,7 +2766,7 @@ var TAXONOMY_LABELS = {
2417
2766
 
2418
2767
  // src/job/job-filter.schema.ts
2419
2768
  var GEO_GROUP = "Geo";
2420
- var JobFilterShape = (0, import_yup21.object)({
2769
+ var JobFilterShape = (0, import_yup22.object)({
2421
2770
  // ── Classification ──────────────────────────────────────────────────────────
2422
2771
  // Closed vocabularies, sent verbatim and matched exactly: a partial match
2423
2772
  // would make "remote" also match a hypothetical "remote_first".
@@ -2560,8 +2909,8 @@ var JobFilterShape = (0, import_yup21.object)({
2560
2909
  // ── Flags ───────────────────────────────────────────────────────────────────
2561
2910
  // No `.default(false)`: an unchecked box must mean "no constraint", not "only
2562
2911
  // the non-featured ones", so absent simply omits the clause.
2563
- isFeatured: (0, import_yup21.boolean)().optional().label("Featured Only"),
2564
- isPromoted: (0, import_yup21.boolean)().optional().label("Promoted Only"),
2912
+ isFeatured: (0, import_yup22.boolean)().optional().label("Featured Only"),
2913
+ isPromoted: (0, import_yup22.boolean)().optional().label("Promoted Only"),
2565
2914
  featuredMarkets: tokens("Featured Markets"),
2566
2915
  // ── Ranges ──────────────────────────────────────────────────────────────────
2567
2916
  /**
@@ -2572,10 +2921,10 @@ var JobFilterShape = (0, import_yup21.object)({
2572
2921
  // can be selected and a bound is meaningless across them. Here the currency is
2573
2922
  // a single sibling field, so the bound is plain and the currency simply
2574
2923
  // narrows which jobs are compared.
2575
- salary: (0, import_yup21.object)({
2576
- min: (0, import_yup21.number)().integer().min(0).optional().default(void 0).label("Salary \u2265"),
2577
- max: (0, import_yup21.number)().integer().min(0).optional().default(void 0).label("Salary \u2264"),
2578
- currency: (0, import_yup21.string)().oneOf(SupportedSalaryCurrencies).optional().label("Currency")
2924
+ salary: (0, import_yup22.object)({
2925
+ min: (0, import_yup22.number)().integer().min(0).optional().default(void 0).label("Salary \u2265"),
2926
+ max: (0, import_yup22.number)().integer().min(0).optional().default(void 0).label("Salary \u2264"),
2927
+ currency: (0, import_yup22.string)().oneOf(SupportedSalaryCurrencies).optional().label("Currency")
2579
2928
  }).optional().default(void 0).label("Salary"),
2580
2929
  /** Posted within this many days. Relative, so a saved filter does not rot. */
2581
2930
  postedWithinDays: days("Posted Within (days)"),
@@ -2584,7 +2933,7 @@ var JobFilterShape = (0, import_yup21.object)({
2584
2933
  * relative `postedWithinDays`. EPOCH MS, matching how the field is stored — a
2585
2934
  * coerced date string would not compare.
2586
2935
  */
2587
- createdAt: (0, import_yup21.object)({
2936
+ createdAt: (0, import_yup22.object)({
2588
2937
  min: epoch("Posted After"),
2589
2938
  max: epoch("Posted Before")
2590
2939
  }).optional().default(void 0).label("Posted Between")
@@ -2638,7 +2987,7 @@ function deriveIndustry(category, subCategories) {
2638
2987
  }
2639
2988
 
2640
2989
  // src/post/post.schema.ts
2641
- var import_yup22 = require("yup");
2990
+ var import_yup23 = require("yup");
2642
2991
 
2643
2992
  // src/post/post.constant.ts
2644
2993
  var PostStatus = /* @__PURE__ */ ((PostStatus2) => {
@@ -2650,41 +2999,41 @@ var PostStatus = /* @__PURE__ */ ((PostStatus2) => {
2650
2999
  var SupportedPostStatuses = Object.values(PostStatus);
2651
3000
 
2652
3001
  // src/post/post.schema.ts
2653
- var PostSchema = (0, import_yup22.object)({
3002
+ var PostSchema = (0, import_yup23.object)({
2654
3003
  /** Headline shown in listings and at the top of the post. */
2655
- title: (0, import_yup22.string)().required().min(5).max(200).label("Title"),
3004
+ title: (0, import_yup23.string)().required().min(5).max(200).label("Title"),
2656
3005
  /** User-friendly, unique URL identifier. */
2657
- slug: (0, import_yup22.string)().required().max(250).label("Slug"),
3006
+ slug: (0, import_yup23.string)().required().max(250).label("Slug"),
2658
3007
  /** Short summary used in listings and meta description fallback. */
2659
- excerpt: (0, import_yup22.string)().max(500).optional().label("Excerpt"),
3008
+ excerpt: (0, import_yup23.string)().max(500).optional().label("Excerpt"),
2660
3009
  /** Rendered HTML source of truth for the post body (sanitized on write/render).
2661
3010
  * Supports rich formatting markdown can't express (image size/alignment, etc). */
2662
- bodyHTML: (0, import_yup22.string)().required().min(1).label("Body (HTML)"),
3011
+ bodyHTML: (0, import_yup23.string)().required().min(1).label("Body (HTML)"),
2663
3012
  /** Optional Markdown mirror of the body (portable text / search fallback).
2664
3013
  * Kept for back-compat and plain-text derivation; bodyHTML is authoritative. */
2665
- bodyMarkdown: (0, import_yup22.string)().optional().label("Body (Markdown)"),
3014
+ bodyMarkdown: (0, import_yup23.string)().optional().label("Body (Markdown)"),
2666
3015
  /** Cover/hero image URL. Not `.url()`-validated: the value is either an
2667
3016
  * uploaded file URL served by this platform (which may be a bare host like
2668
3017
  * `http://localhost:3082/...` in dev, that yup's `.url()` wrongly rejects) or
2669
3018
  * an admin-pasted link. Kept as a plain string; empty ⇒ null. */
2670
- coverImage: (0, import_yup22.string)().nullable().optional().label("Cover image"),
3019
+ coverImage: (0, import_yup23.string)().nullable().optional().label("Cover image"),
2671
3020
  /** Author (user id). */
2672
- authorId: (0, import_yup22.string)().required().label("Author ID"),
3021
+ authorId: (0, import_yup23.string)().required().label("Author ID"),
2673
3022
  /** Free-form tags for filtering and related-posts. */
2674
- tags: (0, import_yup22.array)().of((0, import_yup22.string)().trim().required()).default([]).label("Tags"),
3023
+ tags: (0, import_yup23.array)().of((0, import_yup23.string)().trim().required()).default([]).label("Tags"),
2675
3024
  /** Estimated reading time in minutes (server-derived from body length). */
2676
- readingTimeMinutes: (0, import_yup22.number)().min(0).optional().label("Reading time (minutes)"),
3025
+ readingTimeMinutes: (0, import_yup23.number)().min(0).optional().label("Reading time (minutes)"),
2677
3026
  /** SEO overrides. */
2678
- seoTags: (0, import_yup22.object)({
2679
- description: (0, import_yup22.string)().optional().label("Description"),
2680
- keywords: (0, import_yup22.array)().of((0, import_yup22.string)().required()).optional().label("Keywords")
3027
+ seoTags: (0, import_yup23.object)({
3028
+ description: (0, import_yup23.string)().optional().label("Description"),
3029
+ keywords: (0, import_yup23.array)().of((0, import_yup23.string)().required()).optional().label("Keywords")
2681
3030
  }).nullable().optional().label("SEO Tags"),
2682
3031
  /** When the post was first published (epoch ms). Server-owned. */
2683
- publishedAt: (0, import_yup22.number)().nullable().optional().label("Published at"),
3032
+ publishedAt: (0, import_yup23.number)().nullable().optional().label("Published at"),
2684
3033
  /** Whether the post is featured in the blog listing. */
2685
- isFeatured: (0, import_yup22.boolean)().default(false).optional().label("Is featured"),
3034
+ isFeatured: (0, import_yup23.boolean)().default(false).optional().label("Is featured"),
2686
3035
  /** Publication state. */
2687
- status: (0, import_yup22.string)().oneOf(SupportedPostStatuses).default("draft" /* Draft */).required().label("Status")
3036
+ status: (0, import_yup23.string)().oneOf(SupportedPostStatuses).default("draft" /* Draft */).required().label("Status")
2688
3037
  }).concat(DbDefaultSchema).noUnknown().label("Post");
2689
3038
 
2690
3039
  // src/geo/geo.constant.ts
@@ -2734,11 +3083,11 @@ var GEO_COLLECTIONS = {
2734
3083
  var GEO_SEARCH_MAX = 64;
2735
3084
 
2736
3085
  // src/geo/country.schema.ts
2737
- var import_yup23 = require("yup");
2738
- var CountrySchema = (0, import_yup23.object)({
3086
+ var import_yup24 = require("yup");
3087
+ var CountrySchema = (0, import_yup24.object)({
2739
3088
  /** The upstream dr5hn id. Stable, and the key every upsert matches on. */
2740
- id: (0, import_yup23.number)().integer().required().label("ID"),
2741
- name: (0, import_yup23.string)().trim().required().label("Name"),
3089
+ id: (0, import_yup24.number)().integer().required().label("ID"),
3090
+ name: (0, import_yup24.string)().trim().required().label("Name"),
2742
3091
  /**
2743
3092
  * ISO 3166-1 alpha-2, UPPERCASE (`IN`, `SE`).
2744
3093
  *
@@ -2747,34 +3096,34 @@ var CountrySchema = (0, import_yup23.object)({
2747
3096
  * already clean uppercase alpha-2. A lowercase value here would compare
2748
3097
  * unequal to all of them and silently match nothing.
2749
3098
  */
2750
- countryCode: (0, import_yup23.string)().trim().uppercase().length(2).required().label("Country Code"),
2751
- iso3: (0, import_yup23.string)().trim().uppercase().length(3).optional().nullable().label("ISO3"),
2752
- numericCode: (0, import_yup23.string)().trim().optional().nullable().label("Numeric Code"),
2753
- phonecode: (0, import_yup23.string)().trim().optional().nullable().label("Phone Code"),
2754
- capital: (0, import_yup23.string)().trim().optional().nullable().label("Capital"),
2755
- currency: (0, import_yup23.string)().trim().optional().nullable().label("Currency"),
2756
- currencyName: (0, import_yup23.string)().trim().optional().nullable().label("Currency Name"),
2757
- currencySymbol: (0, import_yup23.string)().trim().optional().nullable().label("Currency Symbol"),
2758
- tld: (0, import_yup23.string)().trim().optional().nullable().label("TLD"),
3099
+ countryCode: (0, import_yup24.string)().trim().uppercase().length(2).required().label("Country Code"),
3100
+ iso3: (0, import_yup24.string)().trim().uppercase().length(3).optional().nullable().label("ISO3"),
3101
+ numericCode: (0, import_yup24.string)().trim().optional().nullable().label("Numeric Code"),
3102
+ phonecode: (0, import_yup24.string)().trim().optional().nullable().label("Phone Code"),
3103
+ capital: (0, import_yup24.string)().trim().optional().nullable().label("Capital"),
3104
+ currency: (0, import_yup24.string)().trim().optional().nullable().label("Currency"),
3105
+ currencyName: (0, import_yup24.string)().trim().optional().nullable().label("Currency Name"),
3106
+ currencySymbol: (0, import_yup24.string)().trim().optional().nullable().label("Currency Symbol"),
3107
+ tld: (0, import_yup24.string)().trim().optional().nullable().label("TLD"),
2759
3108
  /** The name in its own script, e.g. "भारत". Stored, but NOT searchable; see `searchByLocale`. */
2760
- native: (0, import_yup23.string)().trim().optional().nullable().label("Native Name"),
2761
- population: (0, import_yup23.number)().optional().nullable().label("Population"),
2762
- gdp: (0, import_yup23.number)().optional().nullable().label("GDP"),
2763
- region: (0, import_yup23.string)().trim().optional().nullable().label("Region"),
2764
- regionId: (0, import_yup23.number)().integer().optional().nullable().label("Region ID"),
2765
- subregion: (0, import_yup23.string)().trim().optional().nullable().label("Subregion"),
2766
- subregionId: (0, import_yup23.number)().integer().optional().nullable().label("Subregion ID"),
2767
- nationality: (0, import_yup23.string)().trim().optional().nullable().label("Nationality"),
2768
- areaSqKm: (0, import_yup23.number)().optional().nullable().label("Area (sq km)"),
2769
- postalCodeFormat: (0, import_yup23.string)().trim().optional().nullable().label("Postal Code Format"),
2770
- postalCodeRegex: (0, import_yup23.string)().optional().nullable().label("Postal Code Regex"),
2771
- timezones: (0, import_yup23.array)().of(
2772
- (0, import_yup23.object)({
2773
- zoneName: (0, import_yup23.string)().optional().nullable().label("Zone Name"),
2774
- gmtOffset: (0, import_yup23.number)().optional().nullable().label("GMT Offset"),
2775
- gmtOffsetName: (0, import_yup23.string)().optional().nullable().label("GMT Offset Name"),
2776
- abbreviation: (0, import_yup23.string)().optional().nullable().label("Abbreviation"),
2777
- tzName: (0, import_yup23.string)().optional().nullable().label("TZ Name")
3109
+ native: (0, import_yup24.string)().trim().optional().nullable().label("Native Name"),
3110
+ population: (0, import_yup24.number)().optional().nullable().label("Population"),
3111
+ gdp: (0, import_yup24.number)().optional().nullable().label("GDP"),
3112
+ region: (0, import_yup24.string)().trim().optional().nullable().label("Region"),
3113
+ regionId: (0, import_yup24.number)().integer().optional().nullable().label("Region ID"),
3114
+ subregion: (0, import_yup24.string)().trim().optional().nullable().label("Subregion"),
3115
+ subregionId: (0, import_yup24.number)().integer().optional().nullable().label("Subregion ID"),
3116
+ nationality: (0, import_yup24.string)().trim().optional().nullable().label("Nationality"),
3117
+ areaSqKm: (0, import_yup24.number)().optional().nullable().label("Area (sq km)"),
3118
+ postalCodeFormat: (0, import_yup24.string)().trim().optional().nullable().label("Postal Code Format"),
3119
+ postalCodeRegex: (0, import_yup24.string)().optional().nullable().label("Postal Code Regex"),
3120
+ timezones: (0, import_yup24.array)().of(
3121
+ (0, import_yup24.object)({
3122
+ zoneName: (0, import_yup24.string)().optional().nullable().label("Zone Name"),
3123
+ gmtOffset: (0, import_yup24.number)().optional().nullable().label("GMT Offset"),
3124
+ gmtOffsetName: (0, import_yup24.string)().optional().nullable().label("GMT Offset Name"),
3125
+ abbreviation: (0, import_yup24.string)().optional().nullable().label("Abbreviation"),
3126
+ tzName: (0, import_yup24.string)().optional().nullable().label("TZ Name")
2778
3127
  })
2779
3128
  ).optional().nullable().label("Timezones"),
2780
3129
  /**
@@ -2783,7 +3132,7 @@ var CountrySchema = (0, import_yup23.object)({
2783
3132
  * declaring it here would turn a new locale into a validation failure. The
2784
3133
  * VALUES are split per-locale into `searchByLocale`.
2785
3134
  */
2786
- translations: (0, import_yup23.object)().optional().nullable().label("Translations"),
3135
+ translations: (0, import_yup24.object)().optional().nullable().label("Translations"),
2787
3136
  /**
2788
3137
  * Coordinates as NUMBERS.
2789
3138
  *
@@ -2792,10 +3141,10 @@ var CountrySchema = (0, import_yup23.object)({
2792
3141
  * `"9" > "10"` is true, so a bounding-box filter would be quietly wrong rather
2793
3142
  * than failing loudly.
2794
3143
  */
2795
- latitude: (0, import_yup23.number)().optional().nullable().label("Latitude"),
2796
- longitude: (0, import_yup23.number)().optional().nullable().label("Longitude"),
2797
- emoji: (0, import_yup23.string)().optional().nullable().label("Emoji"),
2798
- emojiU: (0, import_yup23.string)().optional().nullable().label("Emoji Unicode"),
3144
+ latitude: (0, import_yup24.number)().optional().nullable().label("Latitude"),
3145
+ longitude: (0, import_yup24.number)().optional().nullable().label("Longitude"),
3146
+ emoji: (0, import_yup24.string)().optional().nullable().label("Emoji"),
3147
+ emojiU: (0, import_yup24.string)().optional().nullable().label("Emoji Unicode"),
2799
3148
  /**
2800
3149
  * The name field searched for the DEFAULT (English) locale: the canonical
2801
3150
  * `name`, and nothing else.
@@ -2805,7 +3154,7 @@ var CountrySchema = (0, import_yup23.object)({
2805
3154
  * field would make every locale's search implicitly a search of all 19
2806
3155
  * languages: a French UI searching "Inde" would also match German "Indien".
2807
3156
  */
2808
- searchDefault: (0, import_yup23.array)().of((0, import_yup23.string)().required()).optional().default([]).label("Search Default"),
3157
+ searchDefault: (0, import_yup24.array)().of((0, import_yup24.string)().required()).optional().default([]).label("Search Default"),
2809
3158
  /**
2810
3159
  * Per-locale name fields, keyed by lowercased BCP-47 tag (`de`, `pt-br`).
2811
3160
  * Each carries ONLY that locale's translation, which is what makes
@@ -2823,21 +3172,21 @@ var CountrySchema = (0, import_yup23.object)({
2823
3172
  * it, zh-CN, tr, ru, uk, pl, hi, ar. **`sv` is absent**, so a Swedish search
2824
3173
  * matches nothing. That is the accepted cost of strict scoping.
2825
3174
  */
2826
- searchByLocale: (0, import_yup23.object)().optional().nullable().label("Search By Locale"),
3175
+ searchByLocale: (0, import_yup24.object)().optional().nullable().label("Search By Locale"),
2827
3176
  /** Epoch ms of the run that last wrote this row. Drives the stale-row prune. */
2828
- syncedAt: (0, import_yup23.number)().optional().nullable().label("Synced At")
3177
+ syncedAt: (0, import_yup24.number)().optional().nullable().label("Synced At")
2829
3178
  }).label("Country");
2830
3179
 
2831
3180
  // src/geo/state.schema.ts
2832
- var import_yup24 = require("yup");
2833
- var StateSchema = (0, import_yup24.object)({
3181
+ var import_yup25 = require("yup");
3182
+ var StateSchema = (0, import_yup25.object)({
2834
3183
  /** The upstream dr5hn id. Stable, and the key every upsert matches on. */
2835
- id: (0, import_yup24.number)().integer().required().label("ID"),
2836
- name: (0, import_yup24.string)().trim().required().label("Name"),
2837
- countryId: (0, import_yup24.number)().integer().optional().nullable().label("Country ID"),
3184
+ id: (0, import_yup25.number)().integer().required().label("ID"),
3185
+ name: (0, import_yup25.string)().trim().required().label("Name"),
3186
+ countryId: (0, import_yup25.number)().integer().optional().nullable().label("Country ID"),
2838
3187
  /** ISO 3166-1 alpha-2 of the parent, UPPERCASE. See `CountrySchema.countryCode`. */
2839
- countryCode: (0, import_yup24.string)().trim().uppercase().length(2).required().label("Country Code"),
2840
- countryName: (0, import_yup24.string)().trim().optional().nullable().label("Country Name"),
3188
+ countryCode: (0, import_yup25.string)().trim().uppercase().length(2).required().label("Country Code"),
3189
+ countryName: (0, import_yup25.string)().trim().optional().nullable().label("Country Name"),
2841
3190
  /**
2842
3191
  * The BARE subdivision code, e.g. `KA` (upstream `iso2`).
2843
3192
  *
@@ -2849,22 +3198,22 @@ var StateSchema = (0, import_yup24.object)({
2849
3198
  * both are stored so the migration has its destination present before any
2850
3199
  * consumer moves.
2851
3200
  */
2852
- stateCode: (0, import_yup24.string)().trim().uppercase().optional().nullable().label("State Code"),
3201
+ stateCode: (0, import_yup25.string)().trim().uppercase().optional().nullable().label("State Code"),
2853
3202
  /** Fully-qualified ISO 3166-2, e.g. `IN-KA` (upstream `iso3166_2`). */
2854
- stateCodeIso: (0, import_yup24.string)().trim().uppercase().optional().nullable().label("ISO 3166-2 Code"),
2855
- fipsCode: (0, import_yup24.string)().trim().optional().nullable().label("FIPS Code"),
3203
+ stateCodeIso: (0, import_yup25.string)().trim().uppercase().optional().nullable().label("ISO 3166-2 Code"),
3204
+ fipsCode: (0, import_yup25.string)().trim().optional().nullable().label("FIPS Code"),
2856
3205
  /** Upstream's own word for the subdivision: "province", "state", "district", … */
2857
- type: (0, import_yup24.string)().trim().optional().nullable().label("Type"),
2858
- level: (0, import_yup24.number)().integer().optional().nullable().label("Level"),
2859
- parentId: (0, import_yup24.number)().integer().optional().nullable().label("Parent ID"),
3206
+ type: (0, import_yup25.string)().trim().optional().nullable().label("Type"),
3207
+ level: (0, import_yup25.number)().integer().optional().nullable().label("Level"),
3208
+ parentId: (0, import_yup25.number)().integer().optional().nullable().label("Parent ID"),
2860
3209
  /** The name in its own script. Stored, but NOT searchable; see `searchByLocale`. */
2861
- native: (0, import_yup24.string)().trim().optional().nullable().label("Native Name"),
3210
+ native: (0, import_yup25.string)().trim().optional().nullable().label("Native Name"),
2862
3211
  /** Numbers, not the strings upstream ships. See `CountrySchema.latitude`. */
2863
- latitude: (0, import_yup24.number)().optional().nullable().label("Latitude"),
2864
- longitude: (0, import_yup24.number)().optional().nullable().label("Longitude"),
2865
- timezone: (0, import_yup24.string)().trim().optional().nullable().label("Timezone"),
3212
+ latitude: (0, import_yup25.number)().optional().nullable().label("Latitude"),
3213
+ longitude: (0, import_yup25.number)().optional().nullable().label("Longitude"),
3214
+ timezone: (0, import_yup25.string)().trim().optional().nullable().label("Timezone"),
2866
3215
  /** Localized names keyed by locale. Unshaped; see `CountrySchema.translations`. */
2867
- translations: (0, import_yup24.object)().optional().nullable().label("Translations"),
3216
+ translations: (0, import_yup25.object)().optional().nullable().label("Translations"),
2868
3217
  /**
2869
3218
  * The name field searched for the DEFAULT (English) locale: the canonical
2870
3219
  * `name`, and nothing else.
@@ -2874,7 +3223,7 @@ var StateSchema = (0, import_yup24.object)({
2874
3223
  * field would make every locale's search implicitly a search of all 19
2875
3224
  * languages: a French UI searching "Inde" would also match German "Indien".
2876
3225
  */
2877
- searchDefault: (0, import_yup24.array)().of((0, import_yup24.string)().required()).optional().default([]).label("Search Default"),
3226
+ searchDefault: (0, import_yup25.array)().of((0, import_yup25.string)().required()).optional().default([]).label("Search Default"),
2878
3227
  /**
2879
3228
  * Per-locale name fields, keyed by lowercased BCP-47 tag (`de`, `pt-br`).
2880
3229
  * Each carries ONLY that locale's translation, which is what makes
@@ -2892,20 +3241,20 @@ var StateSchema = (0, import_yup24.object)({
2892
3241
  * it, zh-CN, tr, ru, uk, pl, hi, ar. **`sv` is absent**, so a Swedish search
2893
3242
  * matches nothing. That is the accepted cost of strict scoping.
2894
3243
  */
2895
- searchByLocale: (0, import_yup24.object)().optional().nullable().label("Search By Locale"),
3244
+ searchByLocale: (0, import_yup25.object)().optional().nullable().label("Search By Locale"),
2896
3245
  /** Epoch ms of the run that last wrote this row. Drives the stale-row prune. */
2897
- syncedAt: (0, import_yup24.number)().optional().nullable().label("Synced At")
3246
+ syncedAt: (0, import_yup25.number)().optional().nullable().label("Synced At")
2898
3247
  }).label("State");
2899
3248
 
2900
3249
  // src/geo/city.schema.ts
2901
- var import_yup25 = require("yup");
2902
- var CitySchema = (0, import_yup25.object)({
3250
+ var import_yup26 = require("yup");
3251
+ var CitySchema = (0, import_yup26.object)({
2903
3252
  /** The upstream dr5hn id. Stable, and the key every upsert matches on. */
2904
- id: (0, import_yup25.number)().integer().required().label("ID"),
2905
- name: (0, import_yup25.string)().trim().required().label("Name"),
2906
- stateId: (0, import_yup25.number)().integer().optional().nullable().label("State ID"),
3253
+ id: (0, import_yup26.number)().integer().required().label("ID"),
3254
+ name: (0, import_yup26.string)().trim().required().label("Name"),
3255
+ stateId: (0, import_yup26.number)().integer().optional().nullable().label("State ID"),
2907
3256
  /** The BARE subdivision code. Only meaningful paired with `countryCode`; see `StateSchema`. */
2908
- stateCode: (0, import_yup25.string)().trim().uppercase().optional().nullable().label("State Code"),
3257
+ stateCode: (0, import_yup26.string)().trim().uppercase().optional().nullable().label("State Code"),
2909
3258
  /**
2910
3259
  * Fully-qualified ISO 3166-2, e.g. `IN-KA`.
2911
3260
  *
@@ -2915,29 +3264,29 @@ var CitySchema = (0, import_yup25.object)({
2915
3264
  * migration can re-point the cities route without a 153k-row re-ingest.
2916
3265
  * Null when either half is missing.
2917
3266
  */
2918
- stateCodeIso: (0, import_yup25.string)().trim().uppercase().optional().nullable().label("ISO 3166-2 Code"),
2919
- stateName: (0, import_yup25.string)().trim().optional().nullable().label("State Name"),
2920
- countryId: (0, import_yup25.number)().integer().optional().nullable().label("Country ID"),
3267
+ stateCodeIso: (0, import_yup26.string)().trim().uppercase().optional().nullable().label("ISO 3166-2 Code"),
3268
+ stateName: (0, import_yup26.string)().trim().optional().nullable().label("State Name"),
3269
+ countryId: (0, import_yup26.number)().integer().optional().nullable().label("Country ID"),
2921
3270
  /** ISO 3166-1 alpha-2 of the parent, UPPERCASE. See `CountrySchema.countryCode`. */
2922
- countryCode: (0, import_yup25.string)().trim().uppercase().length(2).required().label("Country Code"),
2923
- countryName: (0, import_yup25.string)().trim().optional().nullable().label("Country Name"),
3271
+ countryCode: (0, import_yup26.string)().trim().uppercase().length(2).required().label("Country Code"),
3272
+ countryName: (0, import_yup26.string)().trim().optional().nullable().label("Country Name"),
2924
3273
  /** Numbers, not the strings upstream ships. See `CountrySchema.latitude`. */
2925
- latitude: (0, import_yup25.number)().optional().nullable().label("Latitude"),
2926
- longitude: (0, import_yup25.number)().optional().nullable().label("Longitude"),
3274
+ latitude: (0, import_yup26.number)().optional().nullable().label("Latitude"),
3275
+ longitude: (0, import_yup26.number)().optional().nullable().label("Longitude"),
2927
3276
  /** The name in its own script. Stored, but NOT searchable; see `searchByLocale`. */
2928
- native: (0, import_yup25.string)().trim().optional().nullable().label("Native Name"),
2929
- type: (0, import_yup25.string)().trim().optional().nullable().label("Type"),
2930
- level: (0, import_yup25.number)().integer().optional().nullable().label("Level"),
2931
- parentId: (0, import_yup25.number)().integer().optional().nullable().label("Parent ID"),
3277
+ native: (0, import_yup26.string)().trim().optional().nullable().label("Native Name"),
3278
+ type: (0, import_yup26.string)().trim().optional().nullable().label("Type"),
3279
+ level: (0, import_yup26.number)().integer().optional().nullable().label("Level"),
3280
+ parentId: (0, import_yup26.number)().integer().optional().nullable().label("Parent ID"),
2932
3281
  /**
2933
3282
  * Null on most rows. Worth keeping (it is a good relevance signal when
2934
3283
  * present) but never worth SORTING by alone, since a null-heavy sort buries
2935
3284
  * every city upstream has no figure for.
2936
3285
  */
2937
- population: (0, import_yup25.number)().optional().nullable().label("Population"),
2938
- timezone: (0, import_yup25.string)().trim().optional().nullable().label("Timezone"),
3286
+ population: (0, import_yup26.number)().optional().nullable().label("Population"),
3287
+ timezone: (0, import_yup26.string)().trim().optional().nullable().label("Timezone"),
2939
3288
  /** Localized names keyed by locale. Unshaped; see `CountrySchema.translations`. */
2940
- translations: (0, import_yup25.object)().optional().nullable().label("Translations"),
3289
+ translations: (0, import_yup26.object)().optional().nullable().label("Translations"),
2941
3290
  /**
2942
3291
  * The name field searched for the DEFAULT (English) locale: the canonical
2943
3292
  * `name`, and nothing else.
@@ -2947,7 +3296,7 @@ var CitySchema = (0, import_yup25.object)({
2947
3296
  * field would make every locale's search implicitly a search of all 19
2948
3297
  * languages: a French UI searching "Inde" would also match German "Indien".
2949
3298
  */
2950
- searchDefault: (0, import_yup25.array)().of((0, import_yup25.string)().required()).optional().default([]).label("Search Default"),
3299
+ searchDefault: (0, import_yup26.array)().of((0, import_yup26.string)().required()).optional().default([]).label("Search Default"),
2951
3300
  /**
2952
3301
  * Per-locale name fields, keyed by lowercased BCP-47 tag (`de`, `pt-br`).
2953
3302
  * Each carries ONLY that locale's translation, which is what makes
@@ -2965,14 +3314,14 @@ var CitySchema = (0, import_yup25.object)({
2965
3314
  * it, zh-CN, tr, ru, uk, pl, hi, ar. **`sv` is absent**, so a Swedish search
2966
3315
  * matches nothing. That is the accepted cost of strict scoping.
2967
3316
  */
2968
- searchByLocale: (0, import_yup25.object)().optional().nullable().label("Search By Locale"),
3317
+ searchByLocale: (0, import_yup26.object)().optional().nullable().label("Search By Locale"),
2969
3318
  /** Epoch ms of the run that last wrote this row. Drives the stale-row prune. */
2970
- syncedAt: (0, import_yup25.number)().optional().nullable().label("Synced At")
3319
+ syncedAt: (0, import_yup26.number)().optional().nullable().label("Synced At")
2971
3320
  }).label("City");
2972
3321
 
2973
3322
  // src/geo/country-filter.schema.ts
2974
- var import_yup26 = require("yup");
2975
- var CountryFilterShape = (0, import_yup26.object)({
3323
+ var import_yup27 = require("yup");
3324
+ var CountryFilterShape = (0, import_yup27.object)({
2976
3325
  /** ISO 3166-1 alpha-2 codes, e.g. `IN`. Uppercased, matching how they are stored. */
2977
3326
  countryCodes: codeTokens("Country Codes"),
2978
3327
  /** ISO 3166-1 alpha-3 codes, e.g. `IND`. */
@@ -2992,8 +3341,8 @@ var CountryFilterSchema = rejectUnknownKeys(
2992
3341
  );
2993
3342
 
2994
3343
  // src/geo/state-filter.schema.ts
2995
- var import_yup27 = require("yup");
2996
- var StateFilterShape = (0, import_yup27.object)({
3344
+ var import_yup28 = require("yup");
3345
+ var StateFilterShape = (0, import_yup28.object)({
2997
3346
  /** ISO 3166-1 alpha-2 of the parent country, e.g. `IN`. */
2998
3347
  countryCodes: codeTokens("Country Codes"),
2999
3348
  /**
@@ -3023,8 +3372,8 @@ var StateFilterSchema = rejectUnknownKeys(
3023
3372
  );
3024
3373
 
3025
3374
  // src/geo/city-filter.schema.ts
3026
- var import_yup28 = require("yup");
3027
- var CityFilterShape = (0, import_yup28.object)({
3375
+ var import_yup29 = require("yup");
3376
+ var CityFilterShape = (0, import_yup29.object)({
3028
3377
  /** ISO 3166-1 alpha-2 of the parent country, e.g. `IN`. */
3029
3378
  countryCodes: codeTokens("Country Codes"),
3030
3379
  /**
@@ -3041,28 +3390,28 @@ var CityFilterSchema = rejectUnknownKeys(
3041
3390
  );
3042
3391
 
3043
3392
  // src/geo/geo-sync.schema.ts
3044
- var import_yup29 = require("yup");
3045
- var GeoSyncCountsSchema = (0, import_yup29.object)({
3046
- read: (0, import_yup29.number)().integer().min(0).default(0).label("Read"),
3047
- upserted: (0, import_yup29.number)().integer().min(0).default(0).label("Upserted"),
3048
- modified: (0, import_yup29.number)().integer().min(0).default(0).label("Modified"),
3049
- matched: (0, import_yup29.number)().integer().min(0).default(0).label("Matched"),
3393
+ var import_yup30 = require("yup");
3394
+ var GeoSyncCountsSchema = (0, import_yup30.object)({
3395
+ read: (0, import_yup30.number)().integer().min(0).default(0).label("Read"),
3396
+ upserted: (0, import_yup30.number)().integer().min(0).default(0).label("Upserted"),
3397
+ modified: (0, import_yup30.number)().integer().min(0).default(0).label("Modified"),
3398
+ matched: (0, import_yup30.number)().integer().min(0).default(0).label("Matched"),
3050
3399
  /** Rows removed because upstream dropped them. See `prunedSkipped`. */
3051
- pruned: (0, import_yup29.number)().integer().min(0).default(0).label("Pruned"),
3400
+ pruned: (0, import_yup30.number)().integer().min(0).default(0).label("Pruned"),
3052
3401
  /**
3053
3402
  * True when the stale-row prune was REFUSED because it would have removed more
3054
3403
  * than its safety threshold of the collection. A partial fetch that looked
3055
3404
  * like a success must not be able to empty the table, so the run reports the
3056
3405
  * refusal rather than acting on it.
3057
3406
  */
3058
- prunedSkipped: (0, import_yup29.boolean)().default(false).label("Prune Skipped")
3407
+ prunedSkipped: (0, import_yup30.boolean)().default(false).label("Prune Skipped")
3059
3408
  }).label("Sync Counts");
3060
- var GeoSyncRunSchema = (0, import_yup29.object)({
3061
- jobId: (0, import_yup29.string)().required().label("Job ID"),
3062
- status: (0, import_yup29.mixed)().oneOf(SupportedGeoSyncStatuses).required().label("Status"),
3063
- trigger: (0, import_yup29.mixed)().oneOf(SupportedGeoSyncTriggers).optional().label("Trigger"),
3409
+ var GeoSyncRunSchema = (0, import_yup30.object)({
3410
+ jobId: (0, import_yup30.string)().required().label("Job ID"),
3411
+ status: (0, import_yup30.mixed)().oneOf(SupportedGeoSyncStatuses).required().label("Status"),
3412
+ trigger: (0, import_yup30.mixed)().oneOf(SupportedGeoSyncTriggers).optional().label("Trigger"),
3064
3413
  /** Whether the caller asked to run even though the release tag was unchanged. */
3065
- force: (0, import_yup29.boolean)().default(false).label("Force"),
3414
+ force: (0, import_yup30.boolean)().default(false).label("Force"),
3066
3415
  /**
3067
3416
  * The upstream release tag this run fetched, e.g. `v3.2-export.7`.
3068
3417
  *
@@ -3070,13 +3419,13 @@ var GeoSyncRunSchema = (0, import_yup29.object)({
3070
3419
  * cannot tell what version it is fetching is otherwise a failure, not a
3071
3420
  * refresh, so it is not allowed to proceed unattended.
3072
3421
  */
3073
- releaseTag: (0, import_yup29.string)().optional().nullable().label("Release Tag"),
3422
+ releaseTag: (0, import_yup30.string)().optional().nullable().label("Release Tag"),
3074
3423
  /** The tag of the last successful run, i.e. what this one is replacing. */
3075
- previousReleaseTag: (0, import_yup29.string)().optional().nullable().label("Previous Release Tag"),
3076
- startedAt: (0, import_yup29.number)().required().label("Started At"),
3077
- finishedAt: (0, import_yup29.number)().optional().nullable().label("Finished At"),
3078
- durationMs: (0, import_yup29.number)().optional().nullable().label("Duration (ms)"),
3079
- phase: (0, import_yup29.mixed)().oneOf(SupportedGeoSyncPhases).optional().nullable().label("Phase"),
3424
+ previousReleaseTag: (0, import_yup30.string)().optional().nullable().label("Previous Release Tag"),
3425
+ startedAt: (0, import_yup30.number)().required().label("Started At"),
3426
+ finishedAt: (0, import_yup30.number)().optional().nullable().label("Finished At"),
3427
+ durationMs: (0, import_yup30.number)().optional().nullable().label("Duration (ms)"),
3428
+ phase: (0, import_yup30.mixed)().oneOf(SupportedGeoSyncPhases).optional().nullable().label("Phase"),
3080
3429
  /**
3081
3430
  * Live progress for the dashboard bar.
3082
3431
  *
@@ -3084,25 +3433,25 @@ var GeoSyncRunSchema = (0, import_yup29.object)({
3084
3433
  * collection, or a hardcoded fallback on the very first run, since the true
3085
3434
  * total is not known until the stream ends. The UI should render it as one.
3086
3435
  */
3087
- progress: (0, import_yup29.object)({
3088
- collection: (0, import_yup29.string)().optional().nullable().label("Collection"),
3089
- processed: (0, import_yup29.number)().integer().min(0).default(0).label("Processed"),
3090
- total: (0, import_yup29.number)().integer().min(0).optional().nullable().label("Total")
3436
+ progress: (0, import_yup30.object)({
3437
+ collection: (0, import_yup30.string)().optional().nullable().label("Collection"),
3438
+ processed: (0, import_yup30.number)().integer().min(0).default(0).label("Processed"),
3439
+ total: (0, import_yup30.number)().integer().min(0).optional().nullable().label("Total")
3091
3440
  }).optional().nullable().label("Progress"),
3092
- counts: (0, import_yup29.object)({
3441
+ counts: (0, import_yup30.object)({
3093
3442
  countries: GeoSyncCountsSchema.optional().nullable().label("Countries"),
3094
3443
  states: GeoSyncCountsSchema.optional().nullable().label("States"),
3095
3444
  cities: GeoSyncCountsSchema.optional().nullable().label("Cities")
3096
3445
  }).optional().nullable().label("Counts"),
3097
3446
  /** Why a `skipped` run was skipped, in words the dashboard can show verbatim. */
3098
- reason: (0, import_yup29.string)().optional().nullable().label("Reason"),
3447
+ reason: (0, import_yup30.string)().optional().nullable().label("Reason"),
3099
3448
  /** The failure message on a `failed` run. Surfaced verbatim to the admin. */
3100
- error: (0, import_yup29.string)().optional().nullable().label("Error")
3449
+ error: (0, import_yup30.string)().optional().nullable().label("Error")
3101
3450
  }).label("Geo Sync Run");
3102
3451
 
3103
3452
  // src/language/language-filter.schema.ts
3104
- var import_yup30 = require("yup");
3105
- var LanguageFilterShape = (0, import_yup30.object)({
3453
+ var import_yup31 = require("yup");
3454
+ var LanguageFilterShape = (0, import_yup31.object)({
3106
3455
  /** Language names, matched exactly. Free-text matching is `?search=`. */
3107
3456
  names: tokens("Names")
3108
3457
  }).label("Language Filter");
@@ -3112,8 +3461,8 @@ var LanguageFilterSchema = rejectUnknownKeys(
3112
3461
  );
3113
3462
 
3114
3463
  // src/page/page-filter.schema.ts
3115
- var import_yup31 = require("yup");
3116
- var PageFilterShape = (0, import_yup31.object)({
3464
+ var import_yup32 = require("yup");
3465
+ var PageFilterShape = (0, import_yup32.object)({
3117
3466
  /**
3118
3467
  * `type`, NOT `pageType`.
3119
3468
  *
@@ -3133,7 +3482,7 @@ var PageFilterShape = (0, import_yup31.object)({
3133
3482
  // No `.default(false)`: absent means no constraint. Note `isFeatured` is
3134
3483
  // present on only 55 documents, so combining it with `status: ['published']`
3135
3484
  // leaves 11 pages — a legitimate filter that returns almost nothing.
3136
- isFeatured: (0, import_yup31.boolean)().optional().label("Featured Only"),
3485
+ isFeatured: (0, import_yup32.boolean)().optional().label("Featured Only"),
3137
3486
  /** Lowercase (`in`, `se`, `ww`), like groups. See `GroupFilterSchema`. */
3138
3487
  featuredMarkets: tokens("Featured Markets"),
3139
3488
  /** Page SLUGS. A company is matched by slug: a display name breaks on rename. */
@@ -3146,7 +3495,7 @@ var PageFilterSchema = rejectUnknownKeys(
3146
3495
  );
3147
3496
 
3148
3497
  // src/marketing/marketing.schema.ts
3149
- var import_yup33 = require("yup");
3498
+ var import_yup34 = require("yup");
3150
3499
 
3151
3500
  // src/marketing/marketing.constant.ts
3152
3501
  var CampaignStatus = /* @__PURE__ */ ((CampaignStatus2) => {
@@ -3223,8 +3572,8 @@ var SupportedOccurrenceStatuses = Object.values(OccurrenceStatus);
3223
3572
  var CAMPAIGN_MAX_RECIPIENTS_CEILING = 5e3;
3224
3573
 
3225
3574
  // src/user/user-filter.schema.ts
3226
- var import_yup32 = require("yup");
3227
- var UserFilterBranchShape = (0, import_yup32.object)({
3575
+ var import_yup33 = require("yup");
3576
+ var UserFilterBranchShape = (0, import_yup33.object)({
3228
3577
  /**
3229
3578
  * Exact email addresses. An IDENTITY lookup, not a search.
3230
3579
  *
@@ -3244,7 +3593,7 @@ var UserFilterBranchShape = (0, import_yup32.object)({
3244
3593
  * the input keeps the match an indexed equality instead of a regex that would
3245
3594
  * put the scan straight back.
3246
3595
  */
3247
- emails: (0, import_yup32.array)().of((0, import_yup32.string)().required()).optional().default([]).label("Email Addresses"),
3596
+ emails: (0, import_yup33.array)().of((0, import_yup33.string)().required()).optional().default([]).label("Email Addresses"),
3248
3597
  /**
3249
3598
  * Better Auth account ids. An IDENTITY lookup like `emails`, and for the same
3250
3599
  * reason: the caller already knows the exact value.
@@ -3263,9 +3612,9 @@ var UserFilterBranchShape = (0, import_yup32.object)({
3263
3612
  * The admin filter panel builds its controls explicitly rather than from this
3264
3613
  * shape, so declaring it here does not put an account-id box in the UI.
3265
3614
  */
3266
- authAccountIds: (0, import_yup32.array)().of((0, import_yup32.string)().required()).optional().default([]).label("Auth Account IDs"),
3615
+ authAccountIds: (0, import_yup33.array)().of((0, import_yup33.string)().required()).optional().default([]).label("Auth Account IDs"),
3267
3616
  // ── Enum facets (closed vocabularies, chip pickers) ─────────────────────────
3268
- countries: (0, import_yup32.array)().of((0, import_yup32.string)().required()).optional().default([]).label("Countries"),
3617
+ countries: (0, import_yup33.array)().of((0, import_yup33.string)().required()).optional().default([]).label("Countries"),
3269
3618
  roles: enumFacet(SupportedUserRoles, "Roles"),
3270
3619
  // The user's own current seniority (UserSchema.experienceLevel).
3271
3620
  experienceLevels: enumFacet(SupportedExperienceLevels, "Experience Levels"),
@@ -3398,12 +3747,12 @@ var UserFilterBranchShape = (0, import_yup32.object)({
3398
3747
  // it, so the dependency is stated rather than assumed.
3399
3748
  minSalaryFloor: currencyBound("Min Salary \u2265", "salaryCurrencies"),
3400
3749
  minSalaryCeil: currencyBound("Min Salary \u2264", "salaryCurrencies"),
3401
- openToWorkOnly: (0, import_yup32.boolean)().optional().default(false).label("Open to Work Only"),
3402
- remoteExperienceOnly: (0, import_yup32.boolean)().optional().default(false).label("Has Remote Experience"),
3403
- hasResumeOnly: (0, import_yup32.boolean)().optional().default(false).label("Has Resume"),
3404
- onboardingCompletedOnly: (0, import_yup32.boolean)().optional().default(false).label("Onboarding Completed"),
3405
- emailVerifiedOnly: (0, import_yup32.boolean)().optional().default(false).label("Email Verified Only"),
3406
- mobileVerifiedOnly: (0, import_yup32.boolean)().optional().default(false).label("Mobile Verified Only"),
3750
+ openToWorkOnly: (0, import_yup33.boolean)().optional().default(false).label("Open to Work Only"),
3751
+ remoteExperienceOnly: (0, import_yup33.boolean)().optional().default(false).label("Has Remote Experience"),
3752
+ hasResumeOnly: (0, import_yup33.boolean)().optional().default(false).label("Has Resume"),
3753
+ onboardingCompletedOnly: (0, import_yup33.boolean)().optional().default(false).label("Onboarding Completed"),
3754
+ emailVerifiedOnly: (0, import_yup33.boolean)().optional().default(false).label("Email Verified Only"),
3755
+ mobileVerifiedOnly: (0, import_yup33.boolean)().optional().default(false).label("Mobile Verified Only"),
3407
3756
  // ── Lifecycle windows ───────────────────────────────────────────────────────
3408
3757
  // Relative, because a saved list is resolved fresh on every send: a stored
3409
3758
  // absolute epoch would freeze at authoring time and the audience would decay
@@ -3431,10 +3780,10 @@ var UserFilterBranchShape = (0, import_yup32.object)({
3431
3780
  // Radius search around a point, against location.geo. All three required
3432
3781
  // together; validated as a group rather than field-by-field so a half-filled
3433
3782
  // radius cannot silently match everyone.
3434
- withinRadius: (0, import_yup32.object)({
3435
- lat: (0, import_yup32.number)().min(-90).max(90).required().label("Latitude"),
3436
- lng: (0, import_yup32.number)().min(-180).max(180).required().label("Longitude"),
3437
- km: (0, import_yup32.number)().moreThan(0).max(2e4).required().label("Radius (km)")
3783
+ withinRadius: (0, import_yup33.object)({
3784
+ lat: (0, import_yup33.number)().min(-90).max(90).required().label("Latitude"),
3785
+ lng: (0, import_yup33.number)().min(-180).max(180).required().label("Longitude"),
3786
+ km: (0, import_yup33.number)().moreThan(0).max(2e4).required().label("Radius (km)")
3438
3787
  }).optional().default(void 0).label("Within Radius"),
3439
3788
  // ── Signup context (immutable market / locale) ──────────────────────────────
3440
3789
  signupCountries: tokens("Signup Countries"),
@@ -3457,7 +3806,7 @@ var UserFilterBranchShape = (0, import_yup32.object)({
3457
3806
  // materialise the second case on every list that never chose it.
3458
3807
  //
3459
3808
  // @todo CONSENT-FILTER-ENFORCEMENT — nothing enforces consent anywhere now.
3460
- marketingSubscribed: (0, import_yup32.boolean)().optional().label("Marketing Subscribed"),
3809
+ marketingSubscribed: (0, import_yup33.boolean)().optional().label("Marketing Subscribed"),
3461
3810
  jobAlertFrequencies: enumFacet(
3462
3811
  SupportedJobAlertFrequencies,
3463
3812
  "Job Alert Frequency"
@@ -3478,7 +3827,7 @@ var UserFilterBranchShape = (0, import_yup32.object)({
3478
3827
  ),
3479
3828
  // ── Tenure / recency ────────────────────────────────────────────────────────
3480
3829
  // Against workExperiences[].duration and educations[].duration ("YYYY-MM").
3481
- currentlyEmployed: (0, import_yup32.boolean)().optional().default(false).label("Currently Employed"),
3830
+ currentlyEmployed: (0, import_yup33.boolean)().optional().default(false).label("Currently Employed"),
3482
3831
  graduatedWithinMonths: months("Graduated Within (months)"),
3483
3832
  skillUsedWithinMonths: months("Skill Used Within (months)"),
3484
3833
  // The ABSOLUTE bounds the three relative windows above resolve into
@@ -3491,13 +3840,13 @@ var UserFilterBranchShape = (0, import_yup32.object)({
3491
3840
  // meaning that forever, while "within 18 months" keeps meaning what the admin
3492
3841
  // intended. They are declared because the vocabulary must be complete, not
3493
3842
  // because a list should store one.
3494
- graduatedAfter: (0, import_yup32.string)().trim().optional().label("Graduated After"),
3495
- skillUsedAfter: (0, import_yup32.string)().trim().optional().label("Skill Used After"),
3843
+ graduatedAfter: (0, import_yup33.string)().trim().optional().label("Graduated After"),
3844
+ skillUsedAfter: (0, import_yup33.string)().trim().optional().label("Skill Used After"),
3496
3845
  // ── Profile richness ────────────────────────────────────────────────────────
3497
- hasImage: (0, import_yup32.boolean)().optional().default(false).label("Has Photo"),
3498
- hasHeadline: (0, import_yup32.boolean)().optional().default(false).label("Has Headline"),
3499
- hasAboutMe: (0, import_yup32.boolean)().optional().default(false).label("Has About Me"),
3500
- hasMobile: (0, import_yup32.boolean)().optional().default(false).label("Has Mobile Number"),
3846
+ hasImage: (0, import_yup33.boolean)().optional().default(false).label("Has Photo"),
3847
+ hasHeadline: (0, import_yup33.boolean)().optional().default(false).label("Has Headline"),
3848
+ hasAboutMe: (0, import_yup33.boolean)().optional().default(false).label("Has About Me"),
3849
+ hasMobile: (0, import_yup33.boolean)().optional().default(false).label("Has Mobile Number"),
3501
3850
  // Segment by WHICH profile section is missing (profileCompleteness.missing[]),
3502
3851
  // rather than only by the overall percentage.
3503
3852
  missingSections: tokens("Missing Profile Sections"),
@@ -3509,8 +3858,8 @@ var UserFilterBranchShape = (0, import_yup32.object)({
3509
3858
  // data lives in other services' collections. Tri-state: `false` is a real
3510
3859
  // filter ("has NO job alert"), which is the audience an alert nudge wants, so
3511
3860
  // these are `undefined` when unset rather than defaulting to false.
3512
- hasJobAlert: (0, import_yup32.boolean)().optional().default(void 0).label("Has a Job Alert"),
3513
- hasApplied: (0, import_yup32.boolean)().optional().default(void 0).label("Has Applied"),
3861
+ hasJobAlert: (0, import_yup33.boolean)().optional().default(void 0).label("Has a Job Alert"),
3862
+ hasApplied: (0, import_yup33.boolean)().optional().default(void 0).label("Has Applied"),
3514
3863
  appliedWithinDays: days("Applied Within (days)")
3515
3864
  // `search` is NOT here. It is free text over name/email, not a facet: it names
3516
3865
  // no field on a user document, user-service treats it as a RESERVED PARAM
@@ -3523,25 +3872,25 @@ var BRANCH_KEYS = Object.keys(UserFilterBranchShape.fields);
3523
3872
  var UserFilterBranch = rejectUnknownKeys(UserFilterBranchShape, () => BRANCH_KEYS);
3524
3873
  var UserFilterSchema = rejectUnknownKeys(
3525
3874
  UserFilterBranchShape.shape({
3526
- or: (0, import_yup32.array)().of(UserFilterBranch).optional().default([]).label("Or Any Of")
3875
+ or: (0, import_yup33.array)().of(UserFilterBranch).optional().default([]).label("Or Any Of")
3527
3876
  }).label("User Filter"),
3528
3877
  () => [...BRANCH_KEYS, "or"]
3529
3878
  );
3530
3879
 
3531
3880
  // src/marketing/marketing.schema.ts
3532
3881
  var MailingListFilterSchema = UserFilterSchema;
3533
- var MailingListSchema = (0, import_yup33.object)({
3534
- name: (0, import_yup33.string)().required().min(1).max(120).label("Name"),
3535
- description: (0, import_yup33.string)().optional().max(500).label("Description"),
3882
+ var MailingListSchema = (0, import_yup34.object)({
3883
+ name: (0, import_yup34.string)().required().min(1).max(120).label("Name"),
3884
+ description: (0, import_yup34.string)().optional().max(500).label("Description"),
3536
3885
  filter: MailingListFilterSchema.default(
3537
3886
  () => MailingListFilterSchema.getDefault()
3538
3887
  )
3539
3888
  }).label("Mailing List");
3540
- var CampaignTriggerSchema = (0, import_yup33.object)({
3541
- kind: (0, import_yup33.string)().oneOf(SupportedCampaignTriggerKinds).required().default("manual" /* Manual */).label("Trigger"),
3889
+ var CampaignTriggerSchema = (0, import_yup34.object)({
3890
+ kind: (0, import_yup34.string)().oneOf(SupportedCampaignTriggerKinds).required().default("manual" /* Manual */).label("Trigger"),
3542
3891
  // IANA zone, e.g. "Asia/Kolkata". Local wall-clock times are interpreted in it,
3543
3892
  // so occurrences land at the intended hour across DST rather than drifting.
3544
- timezone: (0, import_yup33.string)().optional().default("UTC").label("Timezone").when("kind", {
3893
+ timezone: (0, import_yup34.string)().optional().default("UTC").label("Timezone").when("kind", {
3545
3894
  is: (k) => k !== "manual" /* Manual */,
3546
3895
  then: (s) => s.required()
3547
3896
  }),
@@ -3555,26 +3904,26 @@ var CampaignTriggerSchema = (0, import_yup33.object)({
3555
3904
  is: "schedule" /* Schedule */,
3556
3905
  then: (s) => s.required()
3557
3906
  }),
3558
- interval: (0, import_yup33.object)({
3559
- every: (0, import_yup33.number)().integer().min(1).max(365).required().label("Every"),
3560
- unit: (0, import_yup33.string)().oneOf(SupportedCampaignIntervalUnits).required().label("Unit")
3907
+ interval: (0, import_yup34.object)({
3908
+ every: (0, import_yup34.number)().integer().min(1).max(365).required().label("Every"),
3909
+ unit: (0, import_yup34.string)().oneOf(SupportedCampaignIntervalUnits).required().label("Unit")
3561
3910
  }).optional().default(void 0).label("Repeats").when("kind", {
3562
3911
  is: "schedule" /* Schedule */,
3563
3912
  then: (s) => s.required()
3564
3913
  }),
3565
3914
  /** Local wall-clock send time, "HH:mm" in `timezone`. */
3566
- 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"),
3915
+ timeOfDay: (0, import_yup34.string)().matches(/^([01]\d|2[0-3]):[0-5]\d$/, "Use HH:mm").optional().default("09:00").label("Time of Day"),
3567
3916
  /** For weekly intervals: ISO weekdays, 1 = Monday .. 7 = Sunday. */
3568
- byWeekday: (0, import_yup33.array)().of((0, import_yup33.number)().integer().min(1).max(7).required()).optional().default([]).label("On Days"),
3917
+ byWeekday: (0, import_yup34.array)().of((0, import_yup34.number)().integer().min(1).max(7).required()).optional().default([]).label("On Days"),
3569
3918
  /**
3570
3919
  * For monthly intervals: 1-31, or -1 meaning "last day of the month". A month
3571
3920
  * shorter than the chosen day is clamped to its last day, so 31 in February
3572
3921
  * fires on the 28th/29th rather than being skipped.
3573
3922
  */
3574
- byMonthday: (0, import_yup33.number)().integer().min(-1).max(31).notOneOf([0]).optional().default(void 0).label("Day of Month"),
3575
- end: (0, import_yup33.object)({
3576
- kind: (0, import_yup33.string)().oneOf(SupportedCampaignEndKinds).required().label("Ends"),
3577
- occurrences: (0, import_yup33.number)().integer().min(1).max(1e4).optional().default(void 0).label("After N Sends").when("kind", {
3923
+ byMonthday: (0, import_yup34.number)().integer().min(-1).max(31).notOneOf([0]).optional().default(void 0).label("Day of Month"),
3924
+ end: (0, import_yup34.object)({
3925
+ kind: (0, import_yup34.string)().oneOf(SupportedCampaignEndKinds).required().label("Ends"),
3926
+ occurrences: (0, import_yup34.number)().integer().min(1).max(1e4).optional().default(void 0).label("After N Sends").when("kind", {
3578
3927
  is: "afterOccurrences" /* AfterOccurrences */,
3579
3928
  then: (s) => s.required()
3580
3929
  }),
@@ -3584,8 +3933,8 @@ var CampaignTriggerSchema = (0, import_yup33.object)({
3584
3933
  })
3585
3934
  }).optional().default(() => ({ kind: "never" /* Never */ })).label("End Condition")
3586
3935
  }).label("Trigger");
3587
- var CampaignDataSourceSchema = (0, import_yup33.object)({
3588
- id: (0, import_yup33.string)().required().label("Data Source"),
3936
+ var CampaignDataSourceSchema = (0, import_yup34.object)({
3937
+ id: (0, import_yup34.string)().required().label("Data Source"),
3589
3938
  /**
3590
3939
  * Derive this source's filter per recipient from their profile, instead of
3591
3940
  * using the admin-entered `params` for everyone.
@@ -3600,7 +3949,7 @@ var CampaignDataSourceSchema = (0, import_yup33.object)({
3600
3949
  * un-personalised. Whether a source can honour it at all is declared by the
3601
3950
  * source itself (`supportsPersonalisation`), which this package cannot see.
3602
3951
  */
3603
- personalised: (0, import_yup33.boolean)().optional().label("Personalised"),
3952
+ personalised: (0, import_yup34.boolean)().optional().label("Personalised"),
3604
3953
  /**
3605
3954
  * A free-form bag whose keys are defined by the chosen source's OWN
3606
3955
  * `paramsSchema`, which this package cannot see. Same `lazy` treatment as
@@ -3613,22 +3962,22 @@ var CampaignDataSourceSchema = (0, import_yup33.object)({
3613
3962
  * them against the real source schema in `checkCampaignConfig`, which is the
3614
3963
  * only place that knows what a given source accepts.
3615
3964
  */
3616
- params: (0, import_yup33.lazy)((value) => {
3965
+ params: (0, import_yup34.lazy)((value) => {
3617
3966
  const keys = value && typeof value === "object" ? Object.keys(value) : [];
3618
- return (0, import_yup33.object)(
3619
- Object.fromEntries(keys.map((k) => [k, (0, import_yup33.mixed)().optional()]))
3967
+ return (0, import_yup34.object)(
3968
+ Object.fromEntries(keys.map((k) => [k, (0, import_yup34.mixed)().optional()]))
3620
3969
  ).optional().default(() => ({})).label("Parameters");
3621
3970
  })
3622
3971
  }).label("Data Source");
3623
- var TemplateVariableBindingSchema = (0, import_yup33.object)({
3624
- kind: (0, import_yup33.string)().oneOf(SupportedTemplateVariableBindingKinds).required().label("Source"),
3625
- value: (0, import_yup33.string)().required().min(1).max(2e3).label("Value")
3972
+ var TemplateVariableBindingSchema = (0, import_yup34.object)({
3973
+ kind: (0, import_yup34.string)().oneOf(SupportedTemplateVariableBindingKinds).required().label("Source"),
3974
+ value: (0, import_yup34.string)().required().min(1).max(2e3).label("Value")
3626
3975
  }).label("Template Variable Binding");
3627
- var CampaignSchema = (0, import_yup33.object)({
3628
- name: (0, import_yup33.string)().required().min(1).max(200).label("Campaign Name"),
3976
+ var CampaignSchema = (0, import_yup34.object)({
3977
+ name: (0, import_yup34.string)().required().min(1).max(200).label("Campaign Name"),
3629
3978
  // The template renders its own subject; an override is only needed for the
3630
3979
  // inline-HTML path, hence not required when a template is chosen.
3631
- subject: (0, import_yup33.string)().optional().max(255).label("Subject").when("templateKey", {
3980
+ subject: (0, import_yup34.string)().optional().max(255).label("Subject").when("templateKey", {
3632
3981
  is: (v) => !v,
3633
3982
  then: (s) => s.required().min(1)
3634
3983
  }),
@@ -3639,12 +3988,12 @@ var CampaignSchema = (0, import_yup33.object)({
3639
3988
  * one with a compliance-checked footer and a render test,
3640
3989
  * and hand-pasted HTML that repeats forever is a mistake that repeats forever.
3641
3990
  */
3642
- html: (0, import_yup33.string)().optional().label("HTML Body").when("templateKey", {
3991
+ html: (0, import_yup34.string)().optional().label("HTML Body").when("templateKey", {
3643
3992
  is: (v) => !v,
3644
3993
  then: (s) => s.required().min(1)
3645
3994
  }),
3646
- text: (0, import_yup33.string)().optional().label("Plain-text Body"),
3647
- listId: (0, import_yup33.string)().required().label("Mailing List"),
3995
+ text: (0, import_yup34.string)().optional().label("Plain-text Body"),
3996
+ listId: (0, import_yup34.string)().required().label("Mailing List"),
3648
3997
  /**
3649
3998
  * Where the campaign is in its life. See `CampaignStatus` for the transitions.
3650
3999
  *
@@ -3653,9 +4002,9 @@ var CampaignSchema = (0, import_yup33.object)({
3653
4002
  * value the enum never contained (`scheduled`) reached production rows and
3654
4003
  * rendered in the admin list as though it were real.
3655
4004
  */
3656
- status: (0, import_yup33.string)().oneOf(SupportedCampaignStatuses).optional().default("draft" /* Draft */).label("Status"),
4005
+ status: (0, import_yup34.string)().oneOf(SupportedCampaignStatuses).optional().default("draft" /* Draft */).label("Status"),
3657
4006
  /** Key from email-service's template registry. Mutually exclusive with `html`. */
3658
- templateKey: (0, import_yup33.string)().optional().label("Email Template"),
4007
+ templateKey: (0, import_yup34.string)().optional().label("Email Template"),
3659
4008
  /**
3660
4009
  * One entry per variable the chosen template declares. Validated at save time
3661
4010
  * against the template's own `variables`, so a missing required binding is a
@@ -3670,9 +4019,9 @@ var CampaignSchema = (0, import_yup33.object)({
3670
4019
  * whose required variables all appear unbound. Shaping each entry also means a
3671
4020
  * malformed binding is rejected here rather than at send time.
3672
4021
  */
3673
- bindings: (0, import_yup33.lazy)((value) => {
4022
+ bindings: (0, import_yup34.lazy)((value) => {
3674
4023
  const keys = value && typeof value === "object" ? Object.keys(value) : [];
3675
- return (0, import_yup33.object)(
4024
+ return (0, import_yup34.object)(
3676
4025
  Object.fromEntries(keys.map((k) => [k, TemplateVariableBindingSchema.required()]))
3677
4026
  ).optional().default(() => ({})).label("Template Data");
3678
4027
  }),
@@ -3684,7 +4033,7 @@ var CampaignSchema = (0, import_yup33.object)({
3684
4033
  * `groups.groupCount`), so two sources providing the same name cannot
3685
4034
  * overwrite each other and a template says which one it means.
3686
4035
  */
3687
- dataSources: (0, import_yup33.array)().of(CampaignDataSourceSchema).optional().default(void 0).label("Data Sources"),
4036
+ dataSources: (0, import_yup34.array)().of(CampaignDataSourceSchema).optional().default(void 0).label("Data Sources"),
3688
4037
  trigger: CampaignTriggerSchema.default(
3689
4038
  () => CampaignTriggerSchema.getDefault()
3690
4039
  ),
@@ -3699,7 +4048,7 @@ var CampaignSchema = (0, import_yup33.object)({
3699
4048
  * representable, and the admin list showed exactly that. Activation is a
3700
4049
  * state transition, so it belongs in the state.
3701
4050
  */
3702
- dryRun: (0, import_yup33.boolean)().optional().default(true).label("Dry Run"),
4051
+ dryRun: (0, import_yup34.boolean)().optional().default(true).label("Dry Run"),
3703
4052
  /**
3704
4053
  * Re-eligibility: when someone this campaign already mailed may get it again.
3705
4054
  *
@@ -3707,23 +4056,23 @@ var CampaignSchema = (0, import_yup33.object)({
3707
4056
  * person. `N`: again only after N days. Opt-in, because most recurring
3708
4057
  * campaigns exist to mail the same people repeatedly.
3709
4058
  */
3710
- resendAfterDays: (0, import_yup33.number)().integer().min(0).max(365).nullable().optional().default(void 0).label("Resend After (days)"),
3711
- maxRecipients: (0, import_yup33.number)().integer().min(1).max(CAMPAIGN_MAX_RECIPIENTS_CEILING).optional().default(void 0).label("Max Recipients"),
3712
- utm: (0, import_yup33.object)({
3713
- campaign: (0, import_yup33.string)().optional().max(120).label("utm_campaign"),
3714
- content: (0, import_yup33.string)().optional().max(120).label("utm_content")
4059
+ resendAfterDays: (0, import_yup34.number)().integer().min(0).max(365).nullable().optional().default(void 0).label("Resend After (days)"),
4060
+ maxRecipients: (0, import_yup34.number)().integer().min(1).max(CAMPAIGN_MAX_RECIPIENTS_CEILING).optional().default(void 0).label("Max Recipients"),
4061
+ utm: (0, import_yup34.object)({
4062
+ campaign: (0, import_yup34.string)().optional().max(120).label("utm_campaign"),
4063
+ content: (0, import_yup34.string)().optional().max(120).label("utm_content")
3715
4064
  }).optional().default(void 0).label("Link Tagging")
3716
4065
  }).label("Campaign");
3717
4066
 
3718
4067
  // src/pagination/pagination.schema.ts
3719
- var import_yup34 = require("yup");
3720
- var PaginationSchema = (0, import_yup34.object)().shape({
3721
- page: (0, import_yup34.number)().required().min(1).label("Page").default(1),
3722
- limit: (0, import_yup34.number)().required().min(1).max(100).label("Limit").default(10),
3723
- sortBy: (0, import_yup34.string)().notOneOf([""]).optional().label("SortBy"),
3724
- nextPage: (0, import_yup34.string)().optional().label("Next Page Token"),
3725
- prevPage: (0, import_yup34.string)().optional().label("Previous Page Token"),
3726
- sortDirection: (0, import_yup34.string)().oneOf(["asc", "desc"]).optional().label("Sort Direction")
4068
+ var import_yup35 = require("yup");
4069
+ var PaginationSchema = (0, import_yup35.object)().shape({
4070
+ page: (0, import_yup35.number)().required().min(1).label("Page").default(1),
4071
+ limit: (0, import_yup35.number)().required().min(1).max(100).label("Limit").default(10),
4072
+ sortBy: (0, import_yup35.string)().notOneOf([""]).optional().label("SortBy"),
4073
+ nextPage: (0, import_yup35.string)().optional().label("Next Page Token"),
4074
+ prevPage: (0, import_yup35.string)().optional().label("Previous Page Token"),
4075
+ sortDirection: (0, import_yup35.string)().oneOf(["asc", "desc"]).optional().label("Sort Direction")
3727
4076
  });
3728
4077
 
3729
4078
  // src/pagination/pagination.constant.ts
@@ -3739,7 +4088,7 @@ var DefaultPaginationOptions = {
3739
4088
  };
3740
4089
 
3741
4090
  // src/saved-search/saved-search.schema.ts
3742
- var import_yup35 = require("yup");
4091
+ var import_yup36 = require("yup");
3743
4092
 
3744
4093
  // src/saved-search/saved-search.constant.ts
3745
4094
  var SavedSearchStatus = /* @__PURE__ */ ((SavedSearchStatus2) => {
@@ -3750,21 +4099,21 @@ var SavedSearchStatus = /* @__PURE__ */ ((SavedSearchStatus2) => {
3750
4099
  var SupportedSavedSearchStatuses = Object.values(SavedSearchStatus);
3751
4100
 
3752
4101
  // src/saved-search/saved-search.schema.ts
3753
- var SavedSearchSchema = (0, import_yup35.object)({
4102
+ var SavedSearchSchema = (0, import_yup36.object)({
3754
4103
  /** Public identifier. Server-assigned. */
3755
- shortId: (0, import_yup35.string)().trim().label("Short Id"),
4104
+ shortId: (0, import_yup36.string)().trim().label("Short Id"),
3756
4105
  /**
3757
4106
  * Owner's Better Auth account id. Server-assigned from the verified token,
3758
4107
  * never accepted from a request body.
3759
4108
  */
3760
- ownerUserId: (0, import_yup35.string)().trim().label("Owner"),
4109
+ ownerUserId: (0, import_yup36.string)().trim().label("Owner"),
3761
4110
  /** What the user calls this search, e.g. "Remote React roles". */
3762
- label: (0, import_yup35.string)().trim().max(120).required().label("Label"),
4111
+ label: (0, import_yup36.string)().trim().max(120).required().label("Label"),
3763
4112
  /**
3764
4113
  * The free-text query. Optional because a search can be filters-only ("all
3765
4114
  * remote jobs in Sweden"), which is a legitimate alert to want.
3766
4115
  */
3767
- search: (0, import_yup35.string)().trim().max(500).optional().label("Search Text"),
4116
+ search: (0, import_yup36.string)().trim().max(500).optional().label("Search Text"),
3768
4117
  /**
3769
4118
  * Field filters, in the same `{ field: values }` shape the search endpoint
3770
4119
  * parses from `filter[field]=a,b`. Held as a free-form object rather than a
@@ -3772,7 +4121,7 @@ var SavedSearchSchema = (0, import_yup35.object)({
3772
4121
  * change independently; validating them here would put this package in the
3773
4122
  * business of tracking that vocabulary.
3774
4123
  */
3775
- filter: (0, import_yup35.object)().optional().default({}).label("Filters"),
4124
+ filter: (0, import_yup36.object)().optional().default({}).label("Filters"),
3776
4125
  /**
3777
4126
  * How often to mail matches. `off` keeps the search saved but silent, which is
3778
4127
  * the difference between "I don't want mail" and "delete my search".
@@ -3781,26 +4130,26 @@ var SavedSearchSchema = (0, import_yup35.object)({
3781
4130
  * than defining a second one: two vocabularies for the same concept is how
3782
4131
  * they drift.
3783
4132
  */
3784
- frequency: (0, import_yup35.string)().oneOf(SupportedJobAlertFrequencies).default("off" /* Off */).label("Alert Frequency"),
3785
- status: (0, import_yup35.string)().oneOf(SupportedSavedSearchStatuses).default("active" /* Active */).label("Status"),
4133
+ frequency: (0, import_yup36.string)().oneOf(SupportedJobAlertFrequencies).default("off" /* Off */).label("Alert Frequency"),
4134
+ status: (0, import_yup36.string)().oneOf(SupportedSavedSearchStatuses).default("active" /* Active */).label("Status"),
3786
4135
  /**
3787
4136
  * Epoch ms of the last alert sent for this search. The matching sweep reads it
3788
4137
  * to decide whether the cadence is due, and writes it only AFTER a successful
3789
4138
  * publish, so a failed run retries next tick rather than silently skipping a
3790
4139
  * period.
3791
4140
  */
3792
- lastNotifiedAt: (0, import_yup35.number)().optional().label("Last Notified At"),
4141
+ lastNotifiedAt: (0, import_yup36.number)().optional().label("Last Notified At"),
3793
4142
  /**
3794
4143
  * Epoch ms high-water mark: only jobs created after this are "new" for the
3795
4144
  * next alert. Kept separate from `lastNotifiedAt` because a run that finds no
3796
4145
  * matches should still advance what counts as new, without implying mail was
3797
4146
  * sent.
3798
4147
  */
3799
- lastMatchedAt: (0, import_yup35.number)().optional().label("Last Matched At")
4148
+ lastMatchedAt: (0, import_yup36.number)().optional().label("Last Matched At")
3800
4149
  }).noUnknown().strict();
3801
4150
 
3802
4151
  // src/report/report.schema.ts
3803
- var import_yup36 = require("yup");
4152
+ var import_yup37 = require("yup");
3804
4153
 
3805
4154
  // src/report/report.constant.ts
3806
4155
  var ResourceType = /* @__PURE__ */ ((ResourceType3) => {
@@ -3824,15 +4173,15 @@ var ReportReason = /* @__PURE__ */ ((ReportReason3) => {
3824
4173
  var SupportedReportReasons = Object.values(ReportReason);
3825
4174
 
3826
4175
  // src/report/report.schema.ts
3827
- var ReportSchema = (0, import_yup36.object)({
3828
- type: (0, import_yup36.mixed)().oneOf(SupportedResourceTypes).required().label("Type"),
3829
- resourceId: (0, import_yup36.string)().required().label("Resource ID"),
3830
- reason: (0, import_yup36.mixed)().oneOf(SupportedReportReasons).required("Please choose a reason").label("Reason"),
3831
- comment: (0, import_yup36.string)().max(1e3).optional().label("Comment")
4176
+ var ReportSchema = (0, import_yup37.object)({
4177
+ type: (0, import_yup37.mixed)().oneOf(SupportedResourceTypes).required().label("Type"),
4178
+ resourceId: (0, import_yup37.string)().required().label("Resource ID"),
4179
+ reason: (0, import_yup37.mixed)().oneOf(SupportedReportReasons).required("Please choose a reason").label("Reason"),
4180
+ comment: (0, import_yup37.string)().max(1e3).optional().label("Comment")
3832
4181
  }).label("Report Schema");
3833
4182
 
3834
4183
  // src/feedback/feedback-filter.schema.ts
3835
- var import_yup37 = require("yup");
4184
+ var import_yup38 = require("yup");
3836
4185
 
3837
4186
  // src/feedback/feedback.constant.ts
3838
4187
  var FeedbackType = /* @__PURE__ */ ((FeedbackType3) => {
@@ -3855,7 +4204,7 @@ var SupportedFeedbackStatuses = Object.values(FeedbackStatus);
3855
4204
  var FEEDBACK_MESSAGE_MAX = 4e3;
3856
4205
 
3857
4206
  // src/feedback/feedback-filter.schema.ts
3858
- var FeedbackFilterShape = (0, import_yup37.object)({
4207
+ var FeedbackFilterShape = (0, import_yup38.object)({
3859
4208
  type: enumFacet(SupportedFeedbackTypes, "Type"),
3860
4209
  status: enumFacet(SupportedFeedbackStatuses, "Status")
3861
4210
  }).label("Feedback Filter");
@@ -3865,18 +4214,18 @@ var FeedbackFilterSchema = rejectUnknownKeys(
3865
4214
  );
3866
4215
 
3867
4216
  // src/feedback/feedback.schema.ts
3868
- var import_yup38 = require("yup");
3869
- var FeedbackSchema = (0, import_yup38.object)({
3870
- type: (0, import_yup38.mixed)().oneOf(SupportedFeedbackTypes).required().label("Type"),
3871
- message: (0, import_yup38.string)().trim().required("Please enter a message").max(FEEDBACK_MESSAGE_MAX).label("Message"),
3872
- email: (0, import_yup38.string)().email().optional().label("Email"),
3873
- pageUrl: (0, import_yup38.string)().optional().label("Page URL"),
3874
- status: (0, import_yup38.mixed)().oneOf(SupportedFeedbackStatuses).optional().label("Status")
4217
+ var import_yup39 = require("yup");
4218
+ var FeedbackSchema = (0, import_yup39.object)({
4219
+ type: (0, import_yup39.mixed)().oneOf(SupportedFeedbackTypes).required().label("Type"),
4220
+ message: (0, import_yup39.string)().trim().required("Please enter a message").max(FEEDBACK_MESSAGE_MAX).label("Message"),
4221
+ email: (0, import_yup39.string)().email().optional().label("Email"),
4222
+ pageUrl: (0, import_yup39.string)().optional().label("Page URL"),
4223
+ status: (0, import_yup39.mixed)().oneOf(SupportedFeedbackStatuses).optional().label("Status")
3875
4224
  }).label("Feedback Schema");
3876
4225
 
3877
4226
  // src/skill/skill-filter.schema.ts
3878
- var import_yup39 = require("yup");
3879
- var SkillFilterShape = (0, import_yup39.object)({
4227
+ var import_yup40 = require("yup");
4228
+ var SkillFilterShape = (0, import_yup40.object)({
3880
4229
  /** Display names, matched exactly. Free-text matching is `?search=`. */
3881
4230
  names: tokens("Names"),
3882
4231
  /**
@@ -3894,7 +4243,7 @@ var SkillFilterSchema = rejectUnknownKeys(
3894
4243
  );
3895
4244
 
3896
4245
  // src/billing/billing.schema.ts
3897
- var import_yup40 = require("yup");
4246
+ var import_yup41 = require("yup");
3898
4247
 
3899
4248
  // src/billing/billing.constant.ts
3900
4249
  var PlanKind = /* @__PURE__ */ ((PlanKind2) => {
@@ -3997,60 +4346,60 @@ var GRANT_MAX_LIMIT = 1e5;
3997
4346
  var CONSUME_MAX_UNITS = 1e3;
3998
4347
 
3999
4348
  // src/billing/billing.schema.ts
4000
- var MoneySchema = (0, import_yup40.object)({
4001
- amountMinor: (0, import_yup40.number)().integer().min(0).required().label("Amount (minor units)"),
4349
+ var MoneySchema = (0, import_yup41.object)({
4350
+ amountMinor: (0, import_yup41.number)().integer().min(0).required().label("Amount (minor units)"),
4002
4351
  /**
4003
4352
  * ISO-4217, as returned by Paddle. Deliberately NOT constrained to
4004
4353
  * `SupportedSalaryCurrencies`: that list is the job-salary vocabulary, and
4005
4354
  * Paddle bills in currencies it does not contain. Constraining it here would
4006
4355
  * reject real payments.
4007
4356
  */
4008
- currencyCode: (0, import_yup40.string)().trim().uppercase().required().label("Currency")
4357
+ currencyCode: (0, import_yup41.string)().trim().uppercase().required().label("Currency")
4009
4358
  }).label("Money");
4010
- var PlanGrantSchema = (0, import_yup40.object)({
4359
+ var PlanGrantSchema = (0, import_yup41.object)({
4011
4360
  /**
4012
4361
  * The gated capability, e.g. `job.post`. A free string on purpose: see the
4013
4362
  * note on `FeatureKey` in `billing.constant.ts`. Adding a feature to the
4014
4363
  * paywall must not require releasing this package.
4015
4364
  */
4016
- feature: (0, import_yup40.string)().trim().required().min(1).max(64).label("Feature"),
4017
- type: (0, import_yup40.string)().oneOf(SupportedGrantTypes).required().default("boolean" /* Boolean */).label("Grant Type"),
4365
+ feature: (0, import_yup41.string)().trim().required().min(1).max(64).label("Feature"),
4366
+ type: (0, import_yup41.string)().oneOf(SupportedGrantTypes).required().default("boolean" /* Boolean */).label("Grant Type"),
4018
4367
  /** Uses per period. Required for `quota`, meaningless otherwise. */
4019
- limit: (0, import_yup40.number)().integer().min(1).max(GRANT_MAX_LIMIT).optional().label("Limit").when("type", {
4368
+ limit: (0, import_yup41.number)().integer().min(1).max(GRANT_MAX_LIMIT).optional().label("Limit").when("type", {
4020
4369
  is: "quota" /* Quota */,
4021
4370
  then: (s) => s.required("A quota grant needs a limit")
4022
4371
  }),
4023
- period: (0, import_yup40.string)().oneOf(SupportedQuotaPeriods).optional().default("month" /* Month */).label("Period").when("type", {
4372
+ period: (0, import_yup41.string)().oneOf(SupportedQuotaPeriods).optional().default("month" /* Month */).label("Period").when("type", {
4024
4373
  is: "quota" /* Quota */,
4025
4374
  then: (s) => s.required("A quota grant needs a period")
4026
4375
  }),
4027
4376
  /** Non-expiring uses granted per purchase. Required for `credit`. */
4028
- amount: (0, import_yup40.number)().integer().min(1).max(GRANT_MAX_LIMIT).optional().label("Amount").when("type", {
4377
+ amount: (0, import_yup41.number)().integer().min(1).max(GRANT_MAX_LIMIT).optional().label("Amount").when("type", {
4029
4378
  is: "credit" /* Credit */,
4030
4379
  then: (s) => s.required("A credit grant needs an amount")
4031
4380
  })
4032
4381
  }).label("Plan Grant");
4033
- var PaddlePriceIdsSchema = (0, import_yup40.object)({
4034
- sandbox: (0, import_yup40.array)().of((0, import_yup40.string)().required()).optional().default([]).label("Sandbox Price IDs"),
4035
- live: (0, import_yup40.array)().of((0, import_yup40.string)().required()).optional().default([]).label("Live Price IDs")
4382
+ var PaddlePriceIdsSchema = (0, import_yup41.object)({
4383
+ sandbox: (0, import_yup41.array)().of((0, import_yup41.string)().required()).optional().default([]).label("Sandbox Price IDs"),
4384
+ live: (0, import_yup41.array)().of((0, import_yup41.string)().required()).optional().default([]).label("Live Price IDs")
4036
4385
  }).label("Paddle Price IDs");
4037
- var PlanSchema = (0, import_yup40.object)({
4386
+ var PlanSchema = (0, import_yup41.object)({
4038
4387
  /** Stable slug, e.g. `employer-pro`. The identifier everything else references. */
4039
- key: (0, import_yup40.string)().trim().lowercase().required().min(2).max(64).label("Key"),
4040
- name: (0, import_yup40.string)().required().min(1).max(120).label("Name"),
4041
- description: (0, import_yup40.string)().optional().max(500).label("Description"),
4042
- kind: (0, import_yup40.string)().oneOf(SupportedPlanKinds).required().default("subscription" /* Subscription */).label("Kind"),
4388
+ key: (0, import_yup41.string)().trim().lowercase().required().min(2).max(64).label("Key"),
4389
+ name: (0, import_yup41.string)().required().min(1).max(120).label("Name"),
4390
+ description: (0, import_yup41.string)().optional().max(500).label("Description"),
4391
+ kind: (0, import_yup41.string)().oneOf(SupportedPlanKinds).required().default("subscription" /* Subscription */).label("Kind"),
4043
4392
  /**
4044
4393
  * The line this plan belongs to; a user holds one live subscription per line.
4045
4394
  * Optional on purpose: absent is the default line, so existing plans need no
4046
4395
  * migration and together still read as "one plan at a time".
4047
4396
  */
4048
- audience: (0, import_yup40.string)().oneOf(SupportedPlanAudiences).optional().label("Audience"),
4049
- status: (0, import_yup40.string)().oneOf(SupportedPlanStatuses).required().default("draft" /* Draft */).label("Status"),
4397
+ audience: (0, import_yup41.string)().oneOf(SupportedPlanAudiences).optional().label("Audience"),
4398
+ status: (0, import_yup41.string)().oneOf(SupportedPlanStatuses).required().default("draft" /* Draft */).label("Status"),
4050
4399
  paddlePriceIds: PaddlePriceIdsSchema.default(
4051
4400
  () => PaddlePriceIdsSchema.getDefault()
4052
4401
  ),
4053
- grants: (0, import_yup40.array)().of(PlanGrantSchema.required()).required().min(1, "A plan must grant at least one feature").label("Grants"),
4402
+ grants: (0, import_yup41.array)().of(PlanGrantSchema.required()).required().min(1, "A plan must grant at least one feature").label("Grants"),
4054
4403
  /** Display price for the pricing page. Billing is always Paddle's own figure. */
4055
4404
  price: MoneySchema.optional().default(void 0).label("Price"),
4056
4405
  /**
@@ -4063,416 +4412,84 @@ var PlanSchema = (0, import_yup40.object)({
4063
4412
  * a subscription as "monthly" at the use site rather than defaulting it here,
4064
4413
  * so an unset value stays visible as unset.
4065
4414
  */
4066
- billingPeriod: (0, import_yup40.string)().oneOf(SupportedBillingPeriods).optional().label("Billing Period"),
4415
+ billingPeriod: (0, import_yup41.string)().oneOf(SupportedBillingPeriods).optional().label("Billing Period"),
4067
4416
  /** Ordering on the pricing page, ascending. */
4068
- sortOrder: (0, import_yup40.number)().integer().optional().default(0).label("Sort Order"),
4069
- createdAt: (0, import_yup40.number)().optional().label("Created At"),
4070
- updatedAt: (0, import_yup40.number)().optional().label("Updated At")
4417
+ sortOrder: (0, import_yup41.number)().integer().optional().default(0).label("Sort Order"),
4418
+ createdAt: (0, import_yup41.number)().optional().label("Created At"),
4419
+ updatedAt: (0, import_yup41.number)().optional().label("Updated At")
4071
4420
  }).label("Plan");
4072
- var BillingSubscriptionSchema = (0, import_yup40.object)({
4073
- userId: (0, import_yup40.string)().required().label("User ID"),
4074
- paddleSubscriptionId: (0, import_yup40.string)().required().label("Paddle Subscription ID"),
4075
- paddleCustomerId: (0, import_yup40.string)().required().label("Paddle Customer ID"),
4076
- planKey: (0, import_yup40.string)().required().label("Plan Key"),
4077
- priceIds: (0, import_yup40.array)().of((0, import_yup40.string)().required()).required().label("Price IDs"),
4078
- status: (0, import_yup40.string)().oneOf(SupportedSubscriptionStatuses).required().label("Status"),
4421
+ var BillingSubscriptionSchema = (0, import_yup41.object)({
4422
+ userId: (0, import_yup41.string)().required().label("User ID"),
4423
+ paddleSubscriptionId: (0, import_yup41.string)().required().label("Paddle Subscription ID"),
4424
+ paddleCustomerId: (0, import_yup41.string)().required().label("Paddle Customer ID"),
4425
+ planKey: (0, import_yup41.string)().required().label("Plan Key"),
4426
+ priceIds: (0, import_yup41.array)().of((0, import_yup41.string)().required()).required().label("Price IDs"),
4427
+ status: (0, import_yup41.string)().oneOf(SupportedSubscriptionStatuses).required().label("Status"),
4079
4428
  /** Start of the period the quota is anchored to. */
4080
- currentPeriodStart: (0, import_yup40.number)().optional().label("Current Period Start"),
4081
- currentPeriodEnd: (0, import_yup40.number)().optional().label("Current Period End"),
4429
+ currentPeriodStart: (0, import_yup41.number)().optional().label("Current Period Start"),
4430
+ currentPeriodEnd: (0, import_yup41.number)().optional().label("Current Period End"),
4082
4431
  /** Set when a cancellation is scheduled; access continues until the period ends. */
4083
- canceledAt: (0, import_yup40.number)().optional().label("Canceled At"),
4432
+ canceledAt: (0, import_yup41.number)().optional().label("Canceled At"),
4084
4433
  /**
4085
4434
  * A downgrade booked for the end of the current period: the plan key it moves
4086
4435
  * to at renewal. Upgrades never set this; they apply immediately. Mirrored
4087
4436
  * from the Paddle subscription's own `customData`, so Paddle stays the source
4088
4437
  * of truth.
4089
4438
  */
4090
- pendingPlanKey: (0, import_yup40.string)().optional().label("Pending Plan Key"),
4439
+ pendingPlanKey: (0, import_yup41.string)().optional().label("Pending Plan Key"),
4091
4440
  /** `occurred_at` of the newest Paddle event applied. Guards out-of-order writes. */
4092
- lastEventAt: (0, import_yup40.number)().required().default(0).label("Last Event At"),
4093
- createdAt: (0, import_yup40.number)().optional().label("Created At"),
4094
- updatedAt: (0, import_yup40.number)().optional().label("Updated At")
4441
+ lastEventAt: (0, import_yup41.number)().required().default(0).label("Last Event At"),
4442
+ createdAt: (0, import_yup41.number)().optional().label("Created At"),
4443
+ updatedAt: (0, import_yup41.number)().optional().label("Updated At")
4095
4444
  }).label("Billing Subscription");
4096
- var BillingTransactionSchema = (0, import_yup40.object)({
4097
- userId: (0, import_yup40.string)().required().label("User ID"),
4098
- paddleTransactionId: (0, import_yup40.string)().required().label("Paddle Transaction ID"),
4099
- paddleCustomerId: (0, import_yup40.string)().optional().label("Paddle Customer ID"),
4100
- paddleSubscriptionId: (0, import_yup40.string)().optional().label("Paddle Subscription ID"),
4101
- planKey: (0, import_yup40.string)().optional().label("Plan Key"),
4102
- priceIds: (0, import_yup40.array)().of((0, import_yup40.string)().required()).required().label("Price IDs"),
4103
- status: (0, import_yup40.string)().oneOf(SupportedTransactionStatuses).required().label("Status"),
4445
+ var BillingTransactionSchema = (0, import_yup41.object)({
4446
+ userId: (0, import_yup41.string)().required().label("User ID"),
4447
+ paddleTransactionId: (0, import_yup41.string)().required().label("Paddle Transaction ID"),
4448
+ paddleCustomerId: (0, import_yup41.string)().optional().label("Paddle Customer ID"),
4449
+ paddleSubscriptionId: (0, import_yup41.string)().optional().label("Paddle Subscription ID"),
4450
+ planKey: (0, import_yup41.string)().optional().label("Plan Key"),
4451
+ priceIds: (0, import_yup41.array)().of((0, import_yup41.string)().required()).required().label("Price IDs"),
4452
+ status: (0, import_yup41.string)().oneOf(SupportedTransactionStatuses).required().label("Status"),
4104
4453
  total: MoneySchema.optional().default(void 0).label("Total"),
4105
- billedAt: (0, import_yup40.number)().optional().label("Billed At"),
4106
- lastEventAt: (0, import_yup40.number)().required().default(0).label("Last Event At"),
4107
- createdAt: (0, import_yup40.number)().optional().label("Created At"),
4108
- updatedAt: (0, import_yup40.number)().optional().label("Updated At")
4454
+ billedAt: (0, import_yup41.number)().optional().label("Billed At"),
4455
+ lastEventAt: (0, import_yup41.number)().required().default(0).label("Last Event At"),
4456
+ createdAt: (0, import_yup41.number)().optional().label("Created At"),
4457
+ updatedAt: (0, import_yup41.number)().optional().label("Updated At")
4109
4458
  }).label("Billing Transaction");
4110
- var FeatureEntitlementSchema = (0, import_yup40.object)({
4111
- feature: (0, import_yup40.string)().required().label("Feature"),
4112
- type: (0, import_yup40.string)().oneOf(SupportedGrantTypes).required().label("Grant Type"),
4459
+ var FeatureEntitlementSchema = (0, import_yup41.object)({
4460
+ feature: (0, import_yup41.string)().required().label("Feature"),
4461
+ type: (0, import_yup41.string)().oneOf(SupportedGrantTypes).required().label("Grant Type"),
4113
4462
  /** Whether the feature may be used AT ALL right now. */
4114
- allowed: (0, import_yup40.boolean)().required().default(false).label("Allowed"),
4463
+ allowed: (0, import_yup41.boolean)().required().default(false).label("Allowed"),
4115
4464
  /** Quota ceiling for the period. Absent for boolean grants. */
4116
- limit: (0, import_yup40.number)().integer().optional().label("Limit"),
4117
- used: (0, import_yup40.number)().integer().optional().label("Used"),
4465
+ limit: (0, import_yup41.number)().integer().optional().label("Limit"),
4466
+ used: (0, import_yup41.number)().integer().optional().label("Used"),
4118
4467
  /** Quota remaining plus any credit balance. Absent for boolean grants. */
4119
- remaining: (0, import_yup40.number)().integer().optional().label("Remaining"),
4468
+ remaining: (0, import_yup41.number)().integer().optional().label("Remaining"),
4120
4469
  /** Non-expiring balance from one-off purchases. */
4121
- credits: (0, import_yup40.number)().integer().optional().label("Credits"),
4470
+ credits: (0, import_yup41.number)().integer().optional().label("Credits"),
4122
4471
  /** When the quota next refreshes. */
4123
- resetsAt: (0, import_yup40.number)().optional().label("Resets At")
4472
+ resetsAt: (0, import_yup41.number)().optional().label("Resets At")
4124
4473
  }).label("Feature Entitlement");
4125
- var EntitlementDenialSchema = (0, import_yup40.object)({
4126
- feature: (0, import_yup40.string)().required().label("Feature"),
4127
- reason: (0, import_yup40.string)().oneOf(SupportedEntitlementDenialReasons).required().label("Reason"),
4128
- limit: (0, import_yup40.number)().integer().optional().label("Limit"),
4129
- used: (0, import_yup40.number)().integer().optional().label("Used"),
4130
- resetsAt: (0, import_yup40.number)().optional().label("Resets At"),
4131
- upgradeUrl: (0, import_yup40.string)().optional().label("Upgrade URL")
4474
+ var EntitlementDenialSchema = (0, import_yup41.object)({
4475
+ feature: (0, import_yup41.string)().required().label("Feature"),
4476
+ reason: (0, import_yup41.string)().oneOf(SupportedEntitlementDenialReasons).required().label("Reason"),
4477
+ limit: (0, import_yup41.number)().integer().optional().label("Limit"),
4478
+ used: (0, import_yup41.number)().integer().optional().label("Used"),
4479
+ resetsAt: (0, import_yup41.number)().optional().label("Resets At"),
4480
+ upgradeUrl: (0, import_yup41.string)().optional().label("Upgrade URL")
4132
4481
  }).label("Entitlement Denial");
4133
- var EntitlementConsumeSchema = (0, import_yup40.object)({
4134
- userId: (0, import_yup40.string)().required().label("User ID"),
4135
- feature: (0, import_yup40.string)().trim().required().min(1).max(64).label("Feature"),
4136
- units: (0, import_yup40.number)().integer().min(1).max(CONSUME_MAX_UNITS).required().default(1).label("Units"),
4482
+ var EntitlementConsumeSchema = (0, import_yup41.object)({
4483
+ userId: (0, import_yup41.string)().required().label("User ID"),
4484
+ feature: (0, import_yup41.string)().trim().required().min(1).max(64).label("Feature"),
4485
+ units: (0, import_yup41.number)().integer().min(1).max(CONSUME_MAX_UNITS).required().default(1).label("Units"),
4137
4486
  /**
4138
4487
  * Caller-supplied de-duplication key. Makes a spend exactly-once across the
4139
4488
  * retries that at-least-once delivery and ordinary network failures produce.
4140
4489
  */
4141
- idempotencyKey: (0, import_yup40.string)().trim().required().min(8).max(128).label("Idempotency Key")
4490
+ idempotencyKey: (0, import_yup41.string)().trim().required().min(8).max(128).label("Idempotency Key")
4142
4491
  }).label("Entitlement Consume");
4143
4492
 
4144
- // src/taxonomy/taxonomy.schema.ts
4145
- var import_yup41 = require("yup");
4146
-
4147
- // src/taxonomy/taxonomy.constant.ts
4148
- var SupportedConceptSources = ["seed", "manual", "import"];
4149
- var SupportedTaxonomySources = ["seed", "manual"];
4150
- var SupportedTaxonomyNodeKinds = ["category", "subcategory", "group"];
4151
- var SupportedPrefilterRules = [
4152
- "spoken_language",
4153
- "no_letters",
4154
- "url_or_email",
4155
- "fragment"
4156
- ];
4157
- var SupportedUnmatchedResolutions = ["pending", "added", "rejected"];
4158
- var SupportedUnmatchedReasons = ["ambiguous"];
4159
- var SupportedProposalKinds = ["concept", "alias", "placement"];
4160
- var SupportedProposalFilters = [...SupportedProposalKinds, "uncategorized"];
4161
- var SupportedProposalStatuses = ["draft", "approved", "discarded"];
4162
- var SupportedProposalMessageRoles = ["operator", "model"];
4163
- var SupportedResolverVerdicts = [
4164
- "same",
4165
- "narrower",
4166
- "broader",
4167
- "related",
4168
- "new_concept",
4169
- "out_of_scope"
4170
- ];
4171
- var SupportedAuditStatuses = ["pending", "correct", "wrong"];
4172
- var SupportedReleaseKinds = ["seed", "manual", "review", "auto"];
4173
- var SupportedReleaseStatuses = ["open", "rolled_back"];
4174
- var SupportedMatchTypes = ["exact", "canonical", "fuzzy", "none"];
4175
- var SupportedTaxonomyJobKinds = [
4176
- "describe-concepts",
4177
- "retype-concepts",
4178
- "generate-aliases",
4179
- "alias-backfill",
4180
- "close-gaps",
4181
- "propose-placements",
4182
- "place-unplaced",
4183
- "calibrate-placement",
4184
- "reseed",
4185
- "reindex",
4186
- "calibrate-resolver",
4187
- "golden",
4188
- "resolve-backlog"
4189
- ];
4190
- var SupportedTaxonomyJobStates = ["running", "succeeded", "failed", "cancelled"];
4191
- var CONCEPT_ID_PATTERN = /^[a-z][a-z0-9-]*_[a-z0-9-]+$/;
4192
- var VOCABULARY_ID_PATTERN = /^[a-z][a-z0-9-]{0,31}$/;
4193
- var TAXONOMY_NODE_ID_PATTERN = /^[A-Za-z0-9_]{1,64}$/;
4194
- var CONCEPT_TYPE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
4195
- var SCHEDULE_MIN_HOURS = 1;
4196
- var SCHEDULE_MAX_HOURS = 24 * 30;
4197
-
4198
- // src/taxonomy/taxonomy.schema.ts
4199
- var conceptId = () => (0, import_yup41.string)().matches(CONCEPT_ID_PATTERN, "conceptId must look like <vocabulary>_<slug>");
4200
- var vocabularyId = () => (0, import_yup41.string)().matches(VOCABULARY_ID_PATTERN, "vocabulary must match [a-z][a-z0-9-]{0,31}");
4201
- var nodeId = () => (0, import_yup41.string)().matches(TAXONOMY_NODE_ID_PATTERN, "node ids look like CAT_01_TECH");
4202
- var strings = () => (0, import_yup41.array)((0, import_yup41.string)().trim().min(1).required());
4203
- var VocabularyTypeSchema = (0, import_yup41.object)({
4204
- id: (0, import_yup41.string)().matches(CONCEPT_TYPE_PATTERN, "type ids match [a-z][a-z0-9_]*").required().label("Type ID"),
4205
- /** Shown to the model when it classifies a concept; include examples. */
4206
- description: (0, import_yup41.string)().defined().label("Description")
4207
- }).label("Vocabulary Type");
4208
- var VocabularySchema = (0, import_yup41.object)({
4209
- vocabularyId: vocabularyId().required().label("Vocabulary ID"),
4210
- name: (0, import_yup41.string)().trim().min(1).required().label("Name"),
4211
- /** What one concept is called in prompts and messages: "skill", "role". */
4212
- noun: (0, import_yup41.string)().trim().min(1).required().label("Noun"),
4213
- nounPlural: (0, import_yup41.string)().trim().min(1).required().label("Noun (plural)"),
4214
- /** What belongs in this vocabulary, for prompts. */
4215
- description: (0, import_yup41.string)().trim().min(1).required().label("Description"),
4216
- /** Where its terms are written, for the "swap it in" test: "a job posting". */
4217
- appearsIn: (0, import_yup41.string)().trim().min(1).required().label("Appears In"),
4218
- /** Things that are NOT concepts of this vocabulary, for prompts. */
4219
- outOfScope: (0, import_yup41.string)().trim().min(1).required().label("Out of Scope"),
4220
- types: (0, import_yup41.array)(VocabularyTypeSchema.required()).min(1).required().test(
4221
- "unique-type-ids",
4222
- "type ids must be unique",
4223
- (types) => !types || new Set(types.map((t) => t.id)).size === types.length
4224
- ).label("Types"),
4225
- /** The type a concept gets when nothing more specific is known. One of `types`. */
4226
- defaultType: (0, import_yup41.string)().required().test("default-type-listed", "defaultType must be one of the type ids", function(value) {
4227
- const types = this.parent.types ?? [];
4228
- return types.some((t) => t.id === value);
4229
- }).label("Default Type"),
4230
- prefilter: (0, import_yup41.array)((0, import_yup41.string)().oneOf(SupportedPrefilterRules).required()).default([]).label("Prefilter"),
4231
- /**
4232
- * Words that qualify a term without changing what it names ("fluent",
4233
- * "native"), dropped from its canonical lookup form in THIS vocabulary only.
4234
- */
4235
- qualifiers: strings().optional().label("Qualifiers"),
4236
- /** Extra instructions per model task; each is optional. */
4237
- guidance: (0, import_yup41.object)({
4238
- describe: (0, import_yup41.string)().optional(),
4239
- classify: (0, import_yup41.string)().optional(),
4240
- aliases: (0, import_yup41.string)().optional(),
4241
- verify: (0, import_yup41.string)().optional(),
4242
- place: (0, import_yup41.string)().optional()
4243
- }).label("Guidance"),
4244
- source: (0, import_yup41.string)().oneOf(SupportedTaxonomySources).required().label("Source"),
4245
- createdAt: (0, import_yup41.number)().required().label("Created At"),
4246
- updatedAt: (0, import_yup41.number)().required().label("Updated At")
4247
- }).label("Vocabulary");
4248
- var TaxonomyNodeSchema = (0, import_yup41.object)({
4249
- nodeId: nodeId().required().label("Node ID"),
4250
- vocabulary: vocabularyId().required().label("Vocabulary"),
4251
- kind: (0, import_yup41.string)().oneOf(SupportedTaxonomyNodeKinds).required().label("Kind"),
4252
- name: (0, import_yup41.string)().trim().min(1).required().label("Name"),
4253
- /** Groups carry a description; categories and subcategories usually do not. */
4254
- description: (0, import_yup41.string)().optional().label("Description"),
4255
- aliases: strings().default([]).label("Aliases"),
4256
- /** null for categories (the roots). */
4257
- parentId: (0, import_yup41.string)().nullable().defined().label("Parent ID"),
4258
- /** Ancestor ids from the root down to and including this node. */
4259
- path: (0, import_yup41.array)((0, import_yup41.string)().required()).default([]).label("Path"),
4260
- source: (0, import_yup41.string)().oneOf(SupportedTaxonomySources).optional().label("Source"),
4261
- createdAt: (0, import_yup41.number)().required().label("Created At"),
4262
- updatedAt: (0, import_yup41.number)().required().label("Updated At")
4263
- }).label("Taxonomy Node");
4264
- var ConceptMembershipSchema = (0, import_yup41.object)({
4265
- categoryId: (0, import_yup41.string)().required().label("Category ID"),
4266
- categoryName: (0, import_yup41.string)().required().label("Category Name"),
4267
- subcategoryId: (0, import_yup41.string)().required().label("Subcategory ID"),
4268
- subcategoryName: (0, import_yup41.string)().required().label("Subcategory Name"),
4269
- groupId: (0, import_yup41.string)().required().label("Group ID"),
4270
- groupName: (0, import_yup41.string)().required().label("Group Name"),
4271
- /** The source record this membership came from; '' when written by hand. */
4272
- sourceId: (0, import_yup41.string)().default("").label("Source ID")
4273
- }).label("Concept Membership");
4274
- var ConceptSchema = (0, import_yup41.object)({
4275
- /** `<vocabulary>_<slug>`, e.g. `skills_power-bi`. Primary key. */
4276
- conceptId: conceptId().required().label("Concept ID"),
4277
- vocabulary: vocabularyId().required().label("Vocabulary"),
4278
- name: (0, import_yup41.string)().trim().min(1).max(200).required().label("Name"),
4279
- /** One of the vocabulary's type ids; checked against the vocabulary by the service. */
4280
- type: (0, import_yup41.string)().trim().matches(CONCEPT_TYPE_PATTERN, "type must be a type id").required().label("Type"),
4281
- description: (0, import_yup41.string)().trim().max(2e3).default("").label("Description"),
4282
- aliases: strings().default([]).label("Aliases"),
4283
- memberships: (0, import_yup41.array)(ConceptMembershipSchema.required()).default([]).label("Memberships"),
4284
- /** Always concepts of the same vocabulary. */
4285
- relatedConceptIds: strings().default([]).label("Related Concepts"),
4286
- source: (0, import_yup41.string)().oneOf(SupportedConceptSources).required().label("Source"),
4287
- createdAt: (0, import_yup41.number)().required().label("Created At"),
4288
- updatedAt: (0, import_yup41.number)().required().label("Updated At"),
4289
- /** Resume marker of `retype-concepts`: set once the type was examined. */
4290
- retypedAt: (0, import_yup41.number)().optional().label("Retyped At"),
4291
- /** Resume marker of `generate-aliases`: set even when no alias was added. */
4292
- aliasedAt: (0, import_yup41.number)().optional().label("Aliased At"),
4293
- /** Set while `description` is a stand-in; removed once a real one is written. */
4294
- descriptionPending: (0, import_yup41.boolean)().isTrue().optional().label("Description Pending")
4295
- }).label("Concept");
4296
- var UnmatchedTermSchema = (0, import_yup41.object)({
4297
- vocabulary: vocabularyId().required().label("Vocabulary"),
4298
- /** The raw input as first received. */
4299
- term: (0, import_yup41.string)().required().label("Term"),
4300
- /** The dedupe key, so "React JS" and "react.js" share a row. */
4301
- normalizedTerm: (0, import_yup41.string)().required().label("Normalized Term"),
4302
- count: (0, import_yup41.number)().integer().min(0).required().label("Count"),
4303
- firstSeenAt: (0, import_yup41.number)().required().label("First Seen At"),
4304
- lastSeenAt: (0, import_yup41.number)().required().label("Last Seen At"),
4305
- /** Which callers sent it, capped. */
4306
- sources: (0, import_yup41.array)(
4307
- (0, import_yup41.object)({
4308
- service: (0, import_yup41.string)().required(),
4309
- context: (0, import_yup41.string)().optional()
4310
- }).required()
4311
- ).default([]).label("Sources"),
4312
- resolution: (0, import_yup41.string)().oneOf(SupportedUnmatchedResolutions).required().label("Resolution"),
4313
- resolvedConceptId: (0, import_yup41.string)().optional().label("Resolved Concept"),
4314
- /** `ambiguous`: the concept exists; the caller gave no context that picked one. */
4315
- reason: (0, import_yup41.string)().oneOf(SupportedUnmatchedReasons).optional().label("Reason"),
4316
- /** The concepts an ambiguous term could mean. */
4317
- candidates: (0, import_yup41.array)((0, import_yup41.string)().required()).optional().label("Candidates"),
4318
- /** Who rejected it when it was not a human: `prefilter:<rule>` or `model`. */
4319
- rejectedBy: (0, import_yup41.string)().optional().label("Rejected By")
4320
- }).label("Unmatched Term");
4321
- var ProposalCandidateSchema = (0, import_yup41.object)({
4322
- groupId: (0, import_yup41.string)().required(),
4323
- groupName: (0, import_yup41.string)().required(),
4324
- subcategoryId: (0, import_yup41.string)().required(),
4325
- subcategoryName: (0, import_yup41.string)().required(),
4326
- categoryId: (0, import_yup41.string)().required(),
4327
- categoryName: (0, import_yup41.string)().required(),
4328
- score: (0, import_yup41.number)().required(),
4329
- nearestMembers: (0, import_yup41.array)((0, import_yup41.string)().required()).default([])
4330
- }).label("Proposal Candidate");
4331
- var ProposalMessageSchema = (0, import_yup41.object)({
4332
- role: (0, import_yup41.string)().oneOf(SupportedProposalMessageRoles).required(),
4333
- /** The operator's instruction, or the model's note on what it changed and why. */
4334
- content: (0, import_yup41.string)().defined(),
4335
- /** Which model answered. Absent on operator turns. */
4336
- model: (0, import_yup41.string)().optional(),
4337
- at: (0, import_yup41.number)().required()
4338
- }).label("Proposal Message");
4339
- var SuggestedHomeSchema = (0, import_yup41.object)({
4340
- categoryName: (0, import_yup41.string)().defined(),
4341
- subcategoryName: (0, import_yup41.string)().defined(),
4342
- groupName: (0, import_yup41.string)().defined()
4343
- }).label("Suggested Home");
4344
- var ResolverVerificationSchema = (0, import_yup41.object)({
4345
- verdict: (0, import_yup41.string)().oneOf(SupportedResolverVerdicts).required(),
4346
- /** The shown concept the verdict is about; null for `new_concept` / `out_of_scope`. */
4347
- conceptId: (0, import_yup41.string)().nullable().defined(),
4348
- evidence: (0, import_yup41.string)().defined(),
4349
- /** What the judge took the term to mean, abbreviations expanded. */
4350
- means: (0, import_yup41.string)().optional(),
4351
- /** For `same`: did the independent confirm step agree? */
4352
- consistent: (0, import_yup41.boolean)().optional(),
4353
- confirm: (0, import_yup41.object)({
4354
- answer: (0, import_yup41.string)().oneOf(["yes", "no"]).required(),
4355
- evidence: (0, import_yup41.string)().defined()
4356
- }).optional().default(void 0)
4357
- }).label("Resolver Verification");
4358
- var ConceptProposalSchema = (0, import_yup41.object)({
4359
- proposalId: (0, import_yup41.string)().required().label("Proposal ID"),
4360
- vocabulary: vocabularyId().required().label("Vocabulary"),
4361
- /** The miss this closes; `normalizedTerm` links back to its unmatched row. */
4362
- term: (0, import_yup41.string)().required().label("Term"),
4363
- normalizedTerm: (0, import_yup41.string)().required().label("Normalized Term"),
4364
- /** Demand at proposal time, so the queue ranks without a join. */
4365
- count: (0, import_yup41.number)().integer().min(0).required().label("Count"),
4366
- /** Other misses folded into this draft because they name the same new concept. */
4367
- mergedTerms: (0, import_yup41.array)(
4368
- (0, import_yup41.object)({
4369
- term: (0, import_yup41.string)().required(),
4370
- normalizedTerm: (0, import_yup41.string)().required(),
4371
- count: (0, import_yup41.number)().integer().min(0).required()
4372
- }).required()
4373
- ).optional().label("Merged Terms"),
4374
- /** Absent on drafts that predate the field: read as `concept`. */
4375
- kind: (0, import_yup41.string)().oneOf(SupportedProposalKinds).optional().label("Kind"),
4376
- /** For `alias`: the concept the term names. For `placement`: the concept to place. */
4377
- targetConceptId: (0, import_yup41.string)().optional().label("Target Concept"),
4378
- targetName: (0, import_yup41.string)().optional().label("Target Name"),
4379
- // The draft concept, editable until approved. Empty strings are valid here:
4380
- // an alias draft carries no draft concept of its own.
4381
- name: (0, import_yup41.string)().defined().label("Name"),
4382
- type: (0, import_yup41.string)().defined().label("Type"),
4383
- description: (0, import_yup41.string)().defined().label("Description"),
4384
- aliases: (0, import_yup41.array)((0, import_yup41.string)().required()).default([]).label("Aliases"),
4385
- /** Empty when no group fits: approved that way, the concept goes live uncategorized. */
4386
- memberships: (0, import_yup41.array)(ConceptMembershipSchema.required()).default([]).label("Memberships"),
4387
- suggestedHome: SuggestedHomeSchema.nullable().optional().default(void 0).label("Suggested Home"),
4388
- /** Resolved to concepts that exist; names that matched nothing are dropped. */
4389
- relatedConceptIds: (0, import_yup41.array)((0, import_yup41.string)().required()).default([]).label("Related Concepts"),
4390
- /** Every placement candidate considered, best first. */
4391
- candidates: (0, import_yup41.array)(ProposalCandidateSchema.required()).default([]).label("Candidates"),
4392
- needsReview: (0, import_yup41.boolean)().required().label("Needs Review"),
4393
- reason: (0, import_yup41.string)().optional().label("Reason"),
4394
- verification: ResolverVerificationSchema.optional().default(void 0).label("Verification"),
4395
- /** What the auto-accept gate decided for an alias verdict. */
4396
- autoAccept: (0, import_yup41.object)({
4397
- accepted: (0, import_yup41.boolean)().required(),
4398
- reason: (0, import_yup41.string)().optional(),
4399
- tier: (0, import_yup41.string)().optional(),
4400
- calibrationId: (0, import_yup41.string)().optional(),
4401
- releaseId: (0, import_yup41.string)().optional()
4402
- }).optional().default(void 0).label("Auto Accept"),
4403
- /** Which model drafted it. */
4404
- model: (0, import_yup41.string)().defined().label("Model"),
4405
- /** The refinement conversation, oldest first. */
4406
- messages: (0, import_yup41.array)(ProposalMessageSchema.required()).default([]).label("Messages"),
4407
- status: (0, import_yup41.string)().oneOf(SupportedProposalStatuses).required().label("Status"),
4408
- /** Set when approved: the concept this became. */
4409
- resolvedConceptId: (0, import_yup41.string)().optional().label("Resolved Concept"),
4410
- createdAt: (0, import_yup41.number)().required().label("Created At"),
4411
- updatedAt: (0, import_yup41.number)().required().label("Updated At")
4412
- }).label("Concept Proposal");
4413
- var ReleaseSchema = (0, import_yup41.object)({
4414
- releaseId: (0, import_yup41.string)().required().label("Release ID"),
4415
- kind: (0, import_yup41.string)().oneOf(SupportedReleaseKinds).required().label("Kind"),
4416
- status: (0, import_yup41.string)().oneOf(SupportedReleaseStatuses).required().label("Status"),
4417
- createdBy: (0, import_yup41.string)().required().label("Created By"),
4418
- createdAt: (0, import_yup41.number)().required().label("Created At"),
4419
- note: (0, import_yup41.string)().optional().label("Note"),
4420
- rolledBackAt: (0, import_yup41.number)().optional().label("Rolled Back At"),
4421
- rolledBackBy: (0, import_yup41.string)().optional().label("Rolled Back By")
4422
- }).label("Release");
4423
- var AuditSchema = (0, import_yup41.object)({
4424
- auditId: (0, import_yup41.string)().required().label("Audit ID"),
4425
- releaseId: (0, import_yup41.string)().required().label("Release ID"),
4426
- key: (0, import_yup41.string)().required().label("Key"),
4427
- term: (0, import_yup41.string)().required().label("Term"),
4428
- conceptId: (0, import_yup41.string)().required().label("Concept ID"),
4429
- tier: (0, import_yup41.string)().optional().label("Tier"),
4430
- status: (0, import_yup41.string)().oneOf(SupportedAuditStatuses).required().label("Status"),
4431
- createdAt: (0, import_yup41.number)().required().label("Created At"),
4432
- resolvedAt: (0, import_yup41.number)().optional().label("Resolved At"),
4433
- resolvedBy: (0, import_yup41.string)().optional().label("Resolved By")
4434
- }).label("Audit");
4435
- var ResolverSettingsSchema = (0, import_yup41.object)({
4436
- autoAccept: (0, import_yup41.object)({
4437
- /** Stop switch for WRITING auto keys. Off until a calibration passes. */
4438
- enabled: (0, import_yup41.boolean)().required(),
4439
- /** Minimum calibrated precision (Wilson 95% lower bound) for a tier to auto-accept. */
4440
- target: (0, import_yup41.number)().min(0.5).lessThan(1).required(),
4441
- maxPerRun: (0, import_yup41.number)().integer().min(0).required(),
4442
- maxPerDay: (0, import_yup41.number)().integer().min(0).required(),
4443
- /** Share of each auto release sampled into the audit queue (0..1). */
4444
- auditRate: (0, import_yup41.number)().min(0).max(1).required()
4445
- }).label("Auto Accept"),
4446
- /** Stop switch for SERVING auto keys: false hides them from lookups at once. */
4447
- serveAutoKeys: (0, import_yup41.boolean)().required().label("Serve Auto Keys"),
4448
- /** Minimum Meilisearch ranking score (0..1) for a fuzzy hit to be considered. */
4449
- fuzzyMinScore: (0, import_yup41.number)().min(0).max(1).required().label("Fuzzy Min Score"),
4450
- /** Per-client limit on public search, per minute per replica. 0 disables it. */
4451
- searchRateLimitPerMin: (0, import_yup41.number)().integer().min(0).required().label("Search Rate Limit"),
4452
- /** Master switch for scheduled maintenance jobs. */
4453
- scheduledJobs: (0, import_yup41.boolean)().required().label("Scheduled Jobs"),
4454
- updatedAt: (0, import_yup41.number)().optional().label("Updated At"),
4455
- updatedBy: (0, import_yup41.string)().optional().label("Updated By"),
4456
- /** Why auto-accept was last switched off automatically. */
4457
- trippedReason: (0, import_yup41.string)().optional().label("Tripped Reason")
4458
- }).label("Resolver Settings");
4459
- var TaxonomyScheduleSchema = (0, import_yup41.object)({
4460
- kind: (0, import_yup41.string)().oneOf(SupportedTaxonomyJobKinds).required().label("Job"),
4461
- enabled: (0, import_yup41.boolean)().required().label("Enabled"),
4462
- everyHours: (0, import_yup41.number)().min(SCHEDULE_MIN_HOURS).max(SCHEDULE_MAX_HOURS).required().label("Every (hours)"),
4463
- /** Last time the scheduler fired this job. */
4464
- lastRunAt: (0, import_yup41.number)().optional().label("Last Run At"),
4465
- updatedAt: (0, import_yup41.number)().required().label("Updated At")
4466
- }).label("Taxonomy Schedule");
4467
- var NormalizeContextSchema = (0, import_yup41.object)({
4468
- /** The title of the record the terms come from. */
4469
- title: (0, import_yup41.string)().max(200).optional(),
4470
- /** Backbone node ids (category, subcategory or group). */
4471
- categoryIds: (0, import_yup41.array)(nodeId().required()).max(10).optional(),
4472
- /** Other terms from the same record, in the same vocabulary. */
4473
- siblings: (0, import_yup41.array)((0, import_yup41.string)().max(200).required()).max(50).optional()
4474
- }).label("Normalize Context");
4475
-
4476
4493
  // src/taxonomy/taxonomy-filter.schema.ts
4477
4494
  var import_yup42 = require("yup");
4478
4495
  var ConceptFilterShape = (0, import_yup42.object)({