@thejob/schema 2.6.0 → 2.8.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 +780 -752
- package/dist/index.d.cts +24 -13
- package/dist/index.d.ts +24 -13
- package/dist/index.js +766 -738
- package/package.json +1 -1
- package/src/job/job.schema.ts +28 -5
- package/src/taxonomy/taxonomy.schema.ts +13 -0
package/dist/index.js
CHANGED
|
@@ -370,7 +370,7 @@ var GroupFilterSchema = rejectUnknownKeys(
|
|
|
370
370
|
);
|
|
371
371
|
|
|
372
372
|
// src/job/job.schema.ts
|
|
373
|
-
import { array as
|
|
373
|
+
import { array as array10, boolean as boolean12, lazy, mixed as mixed3, number as number7, object as object19, string as string15 } from "yup";
|
|
374
374
|
|
|
375
375
|
// src/location/location.schema.ts
|
|
376
376
|
import { array as array3, number as number4, object as object6, string as string4 } from "yup";
|
|
@@ -589,6 +589,349 @@ var SkillSchema = object13({
|
|
|
589
589
|
tags: array7().of(string9().max(50)).max(20).optional().label("Tags")
|
|
590
590
|
}).concat(DbDefaultSchema).noUnknown().strict().label("Skill");
|
|
591
591
|
|
|
592
|
+
// src/taxonomy/taxonomy.schema.ts
|
|
593
|
+
import { array as array8, boolean as boolean10, number as number6, object as object14, string as string10 } from "yup";
|
|
594
|
+
|
|
595
|
+
// src/taxonomy/taxonomy.constant.ts
|
|
596
|
+
var SupportedConceptSources = ["seed", "manual", "import"];
|
|
597
|
+
var SupportedTaxonomySources = ["seed", "manual"];
|
|
598
|
+
var SupportedTaxonomyNodeKinds = ["category", "subcategory", "group"];
|
|
599
|
+
var SupportedPrefilterRules = [
|
|
600
|
+
"spoken_language",
|
|
601
|
+
"no_letters",
|
|
602
|
+
"url_or_email",
|
|
603
|
+
"fragment"
|
|
604
|
+
];
|
|
605
|
+
var SupportedUnmatchedResolutions = ["pending", "added", "rejected"];
|
|
606
|
+
var SupportedUnmatchedReasons = ["ambiguous"];
|
|
607
|
+
var SupportedProposalKinds = ["concept", "alias", "placement"];
|
|
608
|
+
var SupportedProposalFilters = [...SupportedProposalKinds, "uncategorized"];
|
|
609
|
+
var SupportedProposalStatuses = ["draft", "approved", "discarded"];
|
|
610
|
+
var SupportedProposalMessageRoles = ["operator", "model"];
|
|
611
|
+
var SupportedResolverVerdicts = [
|
|
612
|
+
"same",
|
|
613
|
+
"narrower",
|
|
614
|
+
"broader",
|
|
615
|
+
"related",
|
|
616
|
+
"new_concept",
|
|
617
|
+
"out_of_scope"
|
|
618
|
+
];
|
|
619
|
+
var SupportedAuditStatuses = ["pending", "correct", "wrong"];
|
|
620
|
+
var SupportedReleaseKinds = ["seed", "manual", "review", "auto"];
|
|
621
|
+
var SupportedReleaseStatuses = ["open", "rolled_back"];
|
|
622
|
+
var SupportedMatchTypes = ["exact", "canonical", "fuzzy", "none"];
|
|
623
|
+
var SupportedTaxonomyJobKinds = [
|
|
624
|
+
"describe-concepts",
|
|
625
|
+
"retype-concepts",
|
|
626
|
+
"generate-aliases",
|
|
627
|
+
"alias-backfill",
|
|
628
|
+
"close-gaps",
|
|
629
|
+
"propose-placements",
|
|
630
|
+
"place-unplaced",
|
|
631
|
+
"calibrate-placement",
|
|
632
|
+
"reseed",
|
|
633
|
+
"reindex",
|
|
634
|
+
"calibrate-resolver",
|
|
635
|
+
"golden",
|
|
636
|
+
"resolve-backlog"
|
|
637
|
+
];
|
|
638
|
+
var SupportedTaxonomyJobStates = ["running", "succeeded", "failed", "cancelled"];
|
|
639
|
+
var CONCEPT_ID_PATTERN = /^[a-z][a-z0-9-]*_[a-z0-9-]+$/;
|
|
640
|
+
var VOCABULARY_ID_PATTERN = /^[a-z][a-z0-9-]{0,31}$/;
|
|
641
|
+
var TAXONOMY_NODE_ID_PATTERN = /^[A-Za-z0-9_]{1,64}$/;
|
|
642
|
+
var CONCEPT_TYPE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
|
|
643
|
+
var SCHEDULE_MIN_HOURS = 1;
|
|
644
|
+
var SCHEDULE_MAX_HOURS = 24 * 30;
|
|
645
|
+
|
|
646
|
+
// src/taxonomy/taxonomy.schema.ts
|
|
647
|
+
var conceptId = () => string10().matches(CONCEPT_ID_PATTERN, "conceptId must look like <vocabulary>_<slug>");
|
|
648
|
+
var vocabularyId = () => string10().matches(VOCABULARY_ID_PATTERN, "vocabulary must match [a-z][a-z0-9-]{0,31}");
|
|
649
|
+
var nodeId = () => string10().matches(TAXONOMY_NODE_ID_PATTERN, "node ids look like CAT_01_TECH");
|
|
650
|
+
var strings = () => array8(string10().trim().min(1).required());
|
|
651
|
+
var VocabularyTypeSchema = object14({
|
|
652
|
+
id: string10().matches(CONCEPT_TYPE_PATTERN, "type ids match [a-z][a-z0-9_]*").required().label("Type ID"),
|
|
653
|
+
/** Shown to the model when it classifies a concept; include examples. */
|
|
654
|
+
description: string10().defined().label("Description")
|
|
655
|
+
}).label("Vocabulary Type");
|
|
656
|
+
var VocabularySchema = object14({
|
|
657
|
+
vocabularyId: vocabularyId().required().label("Vocabulary ID"),
|
|
658
|
+
name: string10().trim().min(1).required().label("Name"),
|
|
659
|
+
/** What one concept is called in prompts and messages: "skill", "role". */
|
|
660
|
+
noun: string10().trim().min(1).required().label("Noun"),
|
|
661
|
+
nounPlural: string10().trim().min(1).required().label("Noun (plural)"),
|
|
662
|
+
/** What belongs in this vocabulary, for prompts. */
|
|
663
|
+
description: string10().trim().min(1).required().label("Description"),
|
|
664
|
+
/** Where its terms are written, for the "swap it in" test: "a job posting". */
|
|
665
|
+
appearsIn: string10().trim().min(1).required().label("Appears In"),
|
|
666
|
+
/** Things that are NOT concepts of this vocabulary, for prompts. */
|
|
667
|
+
outOfScope: string10().trim().min(1).required().label("Out of Scope"),
|
|
668
|
+
types: array8(VocabularyTypeSchema.required()).min(1).required().test(
|
|
669
|
+
"unique-type-ids",
|
|
670
|
+
"type ids must be unique",
|
|
671
|
+
(types) => !types || new Set(types.map((t) => t.id)).size === types.length
|
|
672
|
+
).label("Types"),
|
|
673
|
+
/** The type a concept gets when nothing more specific is known. One of `types`. */
|
|
674
|
+
defaultType: string10().required().test("default-type-listed", "defaultType must be one of the type ids", function(value) {
|
|
675
|
+
const types = this.parent.types ?? [];
|
|
676
|
+
return types.some((t) => t.id === value);
|
|
677
|
+
}).label("Default Type"),
|
|
678
|
+
prefilter: array8(string10().oneOf(SupportedPrefilterRules).required()).default([]).label("Prefilter"),
|
|
679
|
+
/**
|
|
680
|
+
* Words that qualify a term without changing what it names ("fluent",
|
|
681
|
+
* "native"), dropped from its canonical lookup form in THIS vocabulary only.
|
|
682
|
+
*/
|
|
683
|
+
qualifiers: strings().optional().label("Qualifiers"),
|
|
684
|
+
/** Extra instructions per model task; each is optional. */
|
|
685
|
+
guidance: object14({
|
|
686
|
+
describe: string10().optional(),
|
|
687
|
+
classify: string10().optional(),
|
|
688
|
+
aliases: string10().optional(),
|
|
689
|
+
verify: string10().optional(),
|
|
690
|
+
place: string10().optional()
|
|
691
|
+
}).label("Guidance"),
|
|
692
|
+
source: string10().oneOf(SupportedTaxonomySources).required().label("Source"),
|
|
693
|
+
createdAt: number6().required().label("Created At"),
|
|
694
|
+
updatedAt: number6().required().label("Updated At")
|
|
695
|
+
}).label("Vocabulary");
|
|
696
|
+
var TaxonomyNodeSchema = object14({
|
|
697
|
+
nodeId: nodeId().required().label("Node ID"),
|
|
698
|
+
vocabulary: vocabularyId().required().label("Vocabulary"),
|
|
699
|
+
kind: string10().oneOf(SupportedTaxonomyNodeKinds).required().label("Kind"),
|
|
700
|
+
name: string10().trim().min(1).required().label("Name"),
|
|
701
|
+
/** Groups carry a description; categories and subcategories usually do not. */
|
|
702
|
+
description: string10().optional().label("Description"),
|
|
703
|
+
aliases: strings().default([]).label("Aliases"),
|
|
704
|
+
/** null for categories (the roots). */
|
|
705
|
+
parentId: string10().nullable().defined().label("Parent ID"),
|
|
706
|
+
/** Ancestor ids from the root down to and including this node. */
|
|
707
|
+
path: array8(string10().required()).default([]).label("Path"),
|
|
708
|
+
source: string10().oneOf(SupportedTaxonomySources).optional().label("Source"),
|
|
709
|
+
createdAt: number6().required().label("Created At"),
|
|
710
|
+
updatedAt: number6().required().label("Updated At")
|
|
711
|
+
}).label("Taxonomy Node");
|
|
712
|
+
var ConceptMembershipSchema = object14({
|
|
713
|
+
categoryId: string10().required().label("Category ID"),
|
|
714
|
+
categoryName: string10().required().label("Category Name"),
|
|
715
|
+
subcategoryId: string10().required().label("Subcategory ID"),
|
|
716
|
+
subcategoryName: string10().required().label("Subcategory Name"),
|
|
717
|
+
groupId: string10().required().label("Group ID"),
|
|
718
|
+
groupName: string10().required().label("Group Name"),
|
|
719
|
+
/** The source record this membership came from; '' when written by hand. */
|
|
720
|
+
sourceId: string10().default("").label("Source ID")
|
|
721
|
+
}).label("Concept Membership");
|
|
722
|
+
var ConceptSchema = object14({
|
|
723
|
+
/** `<vocabulary>_<slug>`, e.g. `skills_power-bi`. Primary key. */
|
|
724
|
+
conceptId: conceptId().required().label("Concept ID"),
|
|
725
|
+
vocabulary: vocabularyId().required().label("Vocabulary"),
|
|
726
|
+
name: string10().trim().min(1).max(200).required().label("Name"),
|
|
727
|
+
/** One of the vocabulary's type ids; checked against the vocabulary by the service. */
|
|
728
|
+
type: string10().trim().matches(CONCEPT_TYPE_PATTERN, "type must be a type id").required().label("Type"),
|
|
729
|
+
description: string10().trim().max(2e3).default("").label("Description"),
|
|
730
|
+
aliases: strings().default([]).label("Aliases"),
|
|
731
|
+
memberships: array8(ConceptMembershipSchema.required()).default([]).label("Memberships"),
|
|
732
|
+
/** Always concepts of the same vocabulary. */
|
|
733
|
+
relatedConceptIds: strings().default([]).label("Related Concepts"),
|
|
734
|
+
source: string10().oneOf(SupportedConceptSources).required().label("Source"),
|
|
735
|
+
createdAt: number6().required().label("Created At"),
|
|
736
|
+
updatedAt: number6().required().label("Updated At"),
|
|
737
|
+
/** Resume marker of `retype-concepts`: set once the type was examined. */
|
|
738
|
+
retypedAt: number6().optional().label("Retyped At"),
|
|
739
|
+
/** Resume marker of `generate-aliases`: set even when no alias was added. */
|
|
740
|
+
aliasedAt: number6().optional().label("Aliased At"),
|
|
741
|
+
/** Set while `description` is a stand-in; removed once a real one is written. */
|
|
742
|
+
descriptionPending: boolean10().isTrue().optional().label("Description Pending")
|
|
743
|
+
}).label("Concept");
|
|
744
|
+
var UnmatchedTermSchema = object14({
|
|
745
|
+
vocabulary: vocabularyId().required().label("Vocabulary"),
|
|
746
|
+
/** The raw input as first received. */
|
|
747
|
+
term: string10().required().label("Term"),
|
|
748
|
+
/** The dedupe key, so "React JS" and "react.js" share a row. */
|
|
749
|
+
normalizedTerm: string10().required().label("Normalized Term"),
|
|
750
|
+
count: number6().integer().min(0).required().label("Count"),
|
|
751
|
+
firstSeenAt: number6().required().label("First Seen At"),
|
|
752
|
+
lastSeenAt: number6().required().label("Last Seen At"),
|
|
753
|
+
/** Which callers sent it, capped. */
|
|
754
|
+
sources: array8(
|
|
755
|
+
object14({
|
|
756
|
+
service: string10().required(),
|
|
757
|
+
context: string10().optional()
|
|
758
|
+
}).required()
|
|
759
|
+
).default([]).label("Sources"),
|
|
760
|
+
/**
|
|
761
|
+
* Where the term was seen, capped: the caller's title and the terms sent with
|
|
762
|
+
* it. What the resolver reads to tell "CTS" on a chip-design posting from "CTS"
|
|
763
|
+
* on an ad-tech one; a bare term cannot carry that.
|
|
764
|
+
*/
|
|
765
|
+
samples: array8(
|
|
766
|
+
object14({
|
|
767
|
+
title: string10().optional(),
|
|
768
|
+
siblings: array8(string10().required()).default([])
|
|
769
|
+
}).required()
|
|
770
|
+
).default([]).label("Samples"),
|
|
771
|
+
resolution: string10().oneOf(SupportedUnmatchedResolutions).required().label("Resolution"),
|
|
772
|
+
resolvedConceptId: string10().optional().label("Resolved Concept"),
|
|
773
|
+
/** `ambiguous`: the concept exists; the caller gave no context that picked one. */
|
|
774
|
+
reason: string10().oneOf(SupportedUnmatchedReasons).optional().label("Reason"),
|
|
775
|
+
/** The concepts an ambiguous term could mean. */
|
|
776
|
+
candidates: array8(string10().required()).optional().label("Candidates"),
|
|
777
|
+
/** Who rejected it when it was not a human: `prefilter:<rule>` or `model`. */
|
|
778
|
+
rejectedBy: string10().optional().label("Rejected By")
|
|
779
|
+
}).label("Unmatched Term");
|
|
780
|
+
var ProposalCandidateSchema = object14({
|
|
781
|
+
groupId: string10().required(),
|
|
782
|
+
groupName: string10().required(),
|
|
783
|
+
subcategoryId: string10().required(),
|
|
784
|
+
subcategoryName: string10().required(),
|
|
785
|
+
categoryId: string10().required(),
|
|
786
|
+
categoryName: string10().required(),
|
|
787
|
+
score: number6().required(),
|
|
788
|
+
nearestMembers: array8(string10().required()).default([])
|
|
789
|
+
}).label("Proposal Candidate");
|
|
790
|
+
var ProposalMessageSchema = object14({
|
|
791
|
+
role: string10().oneOf(SupportedProposalMessageRoles).required(),
|
|
792
|
+
/** The operator's instruction, or the model's note on what it changed and why. */
|
|
793
|
+
content: string10().defined(),
|
|
794
|
+
/** Which model answered. Absent on operator turns. */
|
|
795
|
+
model: string10().optional(),
|
|
796
|
+
at: number6().required()
|
|
797
|
+
}).label("Proposal Message");
|
|
798
|
+
var SuggestedHomeSchema = object14({
|
|
799
|
+
categoryName: string10().defined(),
|
|
800
|
+
subcategoryName: string10().defined(),
|
|
801
|
+
groupName: string10().defined()
|
|
802
|
+
}).label("Suggested Home");
|
|
803
|
+
var ResolverVerificationSchema = object14({
|
|
804
|
+
verdict: string10().oneOf(SupportedResolverVerdicts).required(),
|
|
805
|
+
/** The shown concept the verdict is about; null for `new_concept` / `out_of_scope`. */
|
|
806
|
+
conceptId: string10().nullable().defined(),
|
|
807
|
+
evidence: string10().defined(),
|
|
808
|
+
/** What the judge took the term to mean, abbreviations expanded. */
|
|
809
|
+
means: string10().optional(),
|
|
810
|
+
/** For `same`: did the independent confirm step agree? */
|
|
811
|
+
consistent: boolean10().optional(),
|
|
812
|
+
confirm: object14({
|
|
813
|
+
answer: string10().oneOf(["yes", "no"]).required(),
|
|
814
|
+
evidence: string10().defined()
|
|
815
|
+
}).optional().default(void 0)
|
|
816
|
+
}).label("Resolver Verification");
|
|
817
|
+
var ConceptProposalSchema = object14({
|
|
818
|
+
proposalId: string10().required().label("Proposal ID"),
|
|
819
|
+
vocabulary: vocabularyId().required().label("Vocabulary"),
|
|
820
|
+
/** The miss this closes; `normalizedTerm` links back to its unmatched row. */
|
|
821
|
+
term: string10().required().label("Term"),
|
|
822
|
+
normalizedTerm: string10().required().label("Normalized Term"),
|
|
823
|
+
/** Demand at proposal time, so the queue ranks without a join. */
|
|
824
|
+
count: number6().integer().min(0).required().label("Count"),
|
|
825
|
+
/** Other misses folded into this draft because they name the same new concept. */
|
|
826
|
+
mergedTerms: array8(
|
|
827
|
+
object14({
|
|
828
|
+
term: string10().required(),
|
|
829
|
+
normalizedTerm: string10().required(),
|
|
830
|
+
count: number6().integer().min(0).required()
|
|
831
|
+
}).required()
|
|
832
|
+
).optional().label("Merged Terms"),
|
|
833
|
+
/** Absent on drafts that predate the field: read as `concept`. */
|
|
834
|
+
kind: string10().oneOf(SupportedProposalKinds).optional().label("Kind"),
|
|
835
|
+
/** For `alias`: the concept the term names. For `placement`: the concept to place. */
|
|
836
|
+
targetConceptId: string10().optional().label("Target Concept"),
|
|
837
|
+
targetName: string10().optional().label("Target Name"),
|
|
838
|
+
// The draft concept, editable until approved. Empty strings are valid here:
|
|
839
|
+
// an alias draft carries no draft concept of its own.
|
|
840
|
+
name: string10().defined().label("Name"),
|
|
841
|
+
type: string10().defined().label("Type"),
|
|
842
|
+
description: string10().defined().label("Description"),
|
|
843
|
+
aliases: array8(string10().required()).default([]).label("Aliases"),
|
|
844
|
+
/** Empty when no group fits: approved that way, the concept goes live uncategorized. */
|
|
845
|
+
memberships: array8(ConceptMembershipSchema.required()).default([]).label("Memberships"),
|
|
846
|
+
suggestedHome: SuggestedHomeSchema.nullable().optional().default(void 0).label("Suggested Home"),
|
|
847
|
+
/** Resolved to concepts that exist; names that matched nothing are dropped. */
|
|
848
|
+
relatedConceptIds: array8(string10().required()).default([]).label("Related Concepts"),
|
|
849
|
+
/** Every placement candidate considered, best first. */
|
|
850
|
+
candidates: array8(ProposalCandidateSchema.required()).default([]).label("Candidates"),
|
|
851
|
+
needsReview: boolean10().required().label("Needs Review"),
|
|
852
|
+
reason: string10().optional().label("Reason"),
|
|
853
|
+
verification: ResolverVerificationSchema.optional().default(void 0).label("Verification"),
|
|
854
|
+
/** What the auto-accept gate decided for an alias verdict. */
|
|
855
|
+
autoAccept: object14({
|
|
856
|
+
accepted: boolean10().required(),
|
|
857
|
+
reason: string10().optional(),
|
|
858
|
+
tier: string10().optional(),
|
|
859
|
+
calibrationId: string10().optional(),
|
|
860
|
+
releaseId: string10().optional()
|
|
861
|
+
}).optional().default(void 0).label("Auto Accept"),
|
|
862
|
+
/** Which model drafted it. */
|
|
863
|
+
model: string10().defined().label("Model"),
|
|
864
|
+
/** The refinement conversation, oldest first. */
|
|
865
|
+
messages: array8(ProposalMessageSchema.required()).default([]).label("Messages"),
|
|
866
|
+
status: string10().oneOf(SupportedProposalStatuses).required().label("Status"),
|
|
867
|
+
/** Set when approved: the concept this became. */
|
|
868
|
+
resolvedConceptId: string10().optional().label("Resolved Concept"),
|
|
869
|
+
createdAt: number6().required().label("Created At"),
|
|
870
|
+
updatedAt: number6().required().label("Updated At")
|
|
871
|
+
}).label("Concept Proposal");
|
|
872
|
+
var ReleaseSchema = object14({
|
|
873
|
+
releaseId: string10().required().label("Release ID"),
|
|
874
|
+
kind: string10().oneOf(SupportedReleaseKinds).required().label("Kind"),
|
|
875
|
+
status: string10().oneOf(SupportedReleaseStatuses).required().label("Status"),
|
|
876
|
+
createdBy: string10().required().label("Created By"),
|
|
877
|
+
createdAt: number6().required().label("Created At"),
|
|
878
|
+
note: string10().optional().label("Note"),
|
|
879
|
+
rolledBackAt: number6().optional().label("Rolled Back At"),
|
|
880
|
+
rolledBackBy: string10().optional().label("Rolled Back By")
|
|
881
|
+
}).label("Release");
|
|
882
|
+
var AuditSchema = object14({
|
|
883
|
+
auditId: string10().required().label("Audit ID"),
|
|
884
|
+
releaseId: string10().required().label("Release ID"),
|
|
885
|
+
key: string10().required().label("Key"),
|
|
886
|
+
term: string10().required().label("Term"),
|
|
887
|
+
conceptId: string10().required().label("Concept ID"),
|
|
888
|
+
tier: string10().optional().label("Tier"),
|
|
889
|
+
status: string10().oneOf(SupportedAuditStatuses).required().label("Status"),
|
|
890
|
+
createdAt: number6().required().label("Created At"),
|
|
891
|
+
resolvedAt: number6().optional().label("Resolved At"),
|
|
892
|
+
resolvedBy: string10().optional().label("Resolved By")
|
|
893
|
+
}).label("Audit");
|
|
894
|
+
var ResolverSettingsSchema = object14({
|
|
895
|
+
autoAccept: object14({
|
|
896
|
+
/** Stop switch for WRITING auto keys. Off until a calibration passes. */
|
|
897
|
+
enabled: boolean10().required(),
|
|
898
|
+
/** Minimum calibrated precision (Wilson 95% lower bound) for a tier to auto-accept. */
|
|
899
|
+
target: number6().min(0.5).lessThan(1).required(),
|
|
900
|
+
maxPerRun: number6().integer().min(0).required(),
|
|
901
|
+
maxPerDay: number6().integer().min(0).required(),
|
|
902
|
+
/** Share of each auto release sampled into the audit queue (0..1). */
|
|
903
|
+
auditRate: number6().min(0).max(1).required()
|
|
904
|
+
}).label("Auto Accept"),
|
|
905
|
+
/** Stop switch for SERVING auto keys: false hides them from lookups at once. */
|
|
906
|
+
serveAutoKeys: boolean10().required().label("Serve Auto Keys"),
|
|
907
|
+
/** Minimum Meilisearch ranking score (0..1) for a fuzzy hit to be considered. */
|
|
908
|
+
fuzzyMinScore: number6().min(0).max(1).required().label("Fuzzy Min Score"),
|
|
909
|
+
/** Per-client limit on public search, per minute per replica. 0 disables it. */
|
|
910
|
+
searchRateLimitPerMin: number6().integer().min(0).required().label("Search Rate Limit"),
|
|
911
|
+
/** Master switch for scheduled maintenance jobs. */
|
|
912
|
+
scheduledJobs: boolean10().required().label("Scheduled Jobs"),
|
|
913
|
+
updatedAt: number6().optional().label("Updated At"),
|
|
914
|
+
updatedBy: string10().optional().label("Updated By"),
|
|
915
|
+
/** Why auto-accept was last switched off automatically. */
|
|
916
|
+
trippedReason: string10().optional().label("Tripped Reason")
|
|
917
|
+
}).label("Resolver Settings");
|
|
918
|
+
var TaxonomyScheduleSchema = object14({
|
|
919
|
+
kind: string10().oneOf(SupportedTaxonomyJobKinds).required().label("Job"),
|
|
920
|
+
enabled: boolean10().required().label("Enabled"),
|
|
921
|
+
everyHours: number6().min(SCHEDULE_MIN_HOURS).max(SCHEDULE_MAX_HOURS).required().label("Every (hours)"),
|
|
922
|
+
/** Last time the scheduler fired this job. */
|
|
923
|
+
lastRunAt: number6().optional().label("Last Run At"),
|
|
924
|
+
updatedAt: number6().required().label("Updated At")
|
|
925
|
+
}).label("Taxonomy Schedule");
|
|
926
|
+
var NormalizeContextSchema = object14({
|
|
927
|
+
/** The title of the record the terms come from. */
|
|
928
|
+
title: string10().max(200).optional(),
|
|
929
|
+
/** Backbone node ids (category, subcategory or group). */
|
|
930
|
+
categoryIds: array8(nodeId().required()).max(10).optional(),
|
|
931
|
+
/** Other terms from the same record, in the same vocabulary. */
|
|
932
|
+
siblings: array8(string10().max(200).required()).max(50).optional()
|
|
933
|
+
}).label("Normalize Context");
|
|
934
|
+
|
|
592
935
|
// src/question/question.constant.ts
|
|
593
936
|
var QuestionType = /* @__PURE__ */ ((QuestionType2) => {
|
|
594
937
|
QuestionType2["Input"] = "input";
|
|
@@ -605,24 +948,24 @@ var AnswerChoiceType = /* @__PURE__ */ ((AnswerChoiceType2) => {
|
|
|
605
948
|
var SupportedAnswerChoiceTypes = Object.values(AnswerChoiceType);
|
|
606
949
|
|
|
607
950
|
// src/question/question.schema.ts
|
|
608
|
-
import { boolean as
|
|
609
|
-
var QuestionSchema =
|
|
610
|
-
label:
|
|
611
|
-
default:
|
|
612
|
-
title:
|
|
613
|
-
type:
|
|
614
|
-
isOptional:
|
|
615
|
-
readonly:
|
|
616
|
-
isNew:
|
|
951
|
+
import { boolean as boolean11, object as object15, string as string11 } from "yup";
|
|
952
|
+
var QuestionSchema = object15().shape({
|
|
953
|
+
label: string11().max(50).optional().label("Label"),
|
|
954
|
+
default: boolean11().optional().default(false).label("Default"),
|
|
955
|
+
title: string11().required().max(200).label("Title"),
|
|
956
|
+
type: string11().oneOf(SupportedQuestionTypes).required().label("Type"),
|
|
957
|
+
isOptional: boolean11().optional().label("Mark if the question is optional"),
|
|
958
|
+
readonly: boolean11().optional().label("Readonly"),
|
|
959
|
+
isNew: boolean11().optional().label("Is New")
|
|
617
960
|
});
|
|
618
961
|
|
|
619
962
|
// src/question/input-question.schema.ts
|
|
620
|
-
import { object as
|
|
963
|
+
import { object as object16, string as string12 } from "yup";
|
|
621
964
|
var InputQuestionSchema = QuestionSchema.concat(
|
|
622
|
-
|
|
623
|
-
type:
|
|
624
|
-
preferredAnswer:
|
|
625
|
-
answers:
|
|
965
|
+
object16().shape({
|
|
966
|
+
type: string12().oneOf(["input" /* Input */]).required().label("Question type"),
|
|
967
|
+
preferredAnswer: string12().max(100).optional().label("Preferred Answer"),
|
|
968
|
+
answers: string12().max(500).when("isOptional", {
|
|
626
969
|
is: (isOptional) => Boolean(isOptional),
|
|
627
970
|
then: (schema) => schema.optional(),
|
|
628
971
|
otherwise: (schema) => schema.required()
|
|
@@ -631,10 +974,10 @@ var InputQuestionSchema = QuestionSchema.concat(
|
|
|
631
974
|
);
|
|
632
975
|
|
|
633
976
|
// src/question/choice-question.schema.ts
|
|
634
|
-
import { array as
|
|
977
|
+
import { array as array9, mixed as mixed2, object as object17, string as string13 } from "yup";
|
|
635
978
|
var ChoicePreferredAnswerSchema = (label = "Preferred answer") => mixed2().when("answerChoiceType", {
|
|
636
979
|
is: (value) => value === "multiple" /* Multiple */,
|
|
637
|
-
then: () =>
|
|
980
|
+
then: () => array9(string13()).test(
|
|
638
981
|
"is-valid-option",
|
|
639
982
|
`${label} must be from the provided options`,
|
|
640
983
|
function(answer) {
|
|
@@ -645,7 +988,7 @@ var ChoicePreferredAnswerSchema = (label = "Preferred answer") => mixed2().when(
|
|
|
645
988
|
return true;
|
|
646
989
|
}
|
|
647
990
|
).optional().label(label),
|
|
648
|
-
otherwise: () =>
|
|
991
|
+
otherwise: () => string13().test(
|
|
649
992
|
"is-valid-option",
|
|
650
993
|
`${label} must be from the provided options`,
|
|
651
994
|
function(answer) {
|
|
@@ -656,18 +999,18 @@ var ChoicePreferredAnswerSchema = (label = "Preferred answer") => mixed2().when(
|
|
|
656
999
|
});
|
|
657
1000
|
var getChoiceAnswerSchema = (answerChoiceType, isOptional) => {
|
|
658
1001
|
if (answerChoiceType === "multiple" /* Multiple */) {
|
|
659
|
-
const multiAnswerSchema =
|
|
1002
|
+
const multiAnswerSchema = array9(string13().max(500));
|
|
660
1003
|
return isOptional ? multiAnswerSchema.optional().default([]) : multiAnswerSchema.min(1, "Select at least one option.").required("Select at least one option.").default([]);
|
|
661
1004
|
}
|
|
662
|
-
const singleAnswerSchema =
|
|
1005
|
+
const singleAnswerSchema = string13().max(500);
|
|
663
1006
|
return isOptional ? singleAnswerSchema.optional() : singleAnswerSchema.required("Select an option.");
|
|
664
1007
|
};
|
|
665
1008
|
var ChoiceQuestionSchema = QuestionSchema.concat(
|
|
666
|
-
|
|
667
|
-
type:
|
|
668
|
-
optionsFrom:
|
|
669
|
-
options:
|
|
670
|
-
answerChoiceType:
|
|
1009
|
+
object17().shape({
|
|
1010
|
+
type: string13().oneOf(["choice" /* Choice */]).required().label("Question type"),
|
|
1011
|
+
optionsFrom: string13().oneOf(["skill" /* Skill */, "education_level" /* EducationLevel */, "experience_level" /* ExperienceLevel */]).optional().label("Option field"),
|
|
1012
|
+
options: array9(string13().required()).min(1).required().label("Options"),
|
|
1013
|
+
answerChoiceType: string13().oneOf(SupportedAnswerChoiceTypes).required().default("single" /* Single */).label("Type"),
|
|
671
1014
|
preferredAnswer: ChoicePreferredAnswerSchema("Preferred answer"),
|
|
672
1015
|
answers: mixed2().when(
|
|
673
1016
|
["answerChoiceType", "isOptional"],
|
|
@@ -677,12 +1020,12 @@ var ChoiceQuestionSchema = QuestionSchema.concat(
|
|
|
677
1020
|
);
|
|
678
1021
|
|
|
679
1022
|
// src/question/read-and-acknowledge.schema.ts
|
|
680
|
-
import { object as
|
|
681
|
-
var AcknowledgeAnswerSchema =
|
|
1023
|
+
import { object as object18, string as string14 } from "yup";
|
|
1024
|
+
var AcknowledgeAnswerSchema = string14().oneOf(["yes" /* Yes */, "no" /* No */]).required("This question must be acknowledged.");
|
|
682
1025
|
var ReadAndAcknowledgeQuestionSchema = QuestionSchema.concat(
|
|
683
|
-
|
|
684
|
-
type:
|
|
685
|
-
description:
|
|
1026
|
+
object18().shape({
|
|
1027
|
+
type: string14().oneOf(["readAndAcknowledge" /* ReadAndAcknowledge */]).required().label("Question type"),
|
|
1028
|
+
description: string14().max(1e4).required().label("Content"),
|
|
686
1029
|
preferredAnswer: AcknowledgeAnswerSchema.label("Preferred Answer"),
|
|
687
1030
|
answers: AcknowledgeAnswerSchema.label("Answer")
|
|
688
1031
|
})
|
|
@@ -763,20 +1106,20 @@ var SupportedApplicationReceivePreferences = Object.values(
|
|
|
763
1106
|
);
|
|
764
1107
|
|
|
765
1108
|
// src/job/job.schema.ts
|
|
766
|
-
var JobSchema =
|
|
1109
|
+
var JobSchema = object19({
|
|
767
1110
|
/** Unique id usable by external systems to reference the job. */
|
|
768
|
-
uniqueId:
|
|
1111
|
+
uniqueId: string15().optional().label("Unique ID"),
|
|
769
1112
|
/** Job title shown in listings and search. */
|
|
770
|
-
headline:
|
|
1113
|
+
headline: string15().required().min(5).max(150).label("Headline"),
|
|
771
1114
|
/** User-friendly, unique URL identifier. */
|
|
772
|
-
slug:
|
|
1115
|
+
slug: string15().required().max(250).label("Slug"),
|
|
773
1116
|
/** How applications are received (inbox vs external website). */
|
|
774
1117
|
applicationReceivePreference: mixed3().oneOf([
|
|
775
1118
|
"inbox" /* Inbox */,
|
|
776
1119
|
"external_website" /* ExternalWebsite */
|
|
777
1120
|
]).required().label("Preference"),
|
|
778
1121
|
/** Required when applicationReceivePreference is ExternalWebsite. */
|
|
779
|
-
externalApplyLink:
|
|
1122
|
+
externalApplyLink: string15().when("applicationReceivePreference", {
|
|
780
1123
|
is: (value) => value === "external_website" /* ExternalWebsite */,
|
|
781
1124
|
then: (schema) => schema.url().required().label("Apply for Job link"),
|
|
782
1125
|
otherwise: (schema) => schema.optional()
|
|
@@ -784,35 +1127,44 @@ var JobSchema = object18({
|
|
|
784
1127
|
/** Company the job belongs to (subset of PageSchema). */
|
|
785
1128
|
company: PageSchema.pick(["name", "slug", "type", "logo"]).deepPartial().concat(PageSchema.pick(["name", "type"])).required().label("Company"),
|
|
786
1129
|
/** @deprecated kept for backwards compatibility. */
|
|
787
|
-
designation:
|
|
788
|
-
employmentType:
|
|
789
|
-
workMode:
|
|
1130
|
+
designation: string15().optional().nullable().label("Designation"),
|
|
1131
|
+
employmentType: string15().required().label("Employment type"),
|
|
1132
|
+
workMode: string15().required().label("Work mode"),
|
|
790
1133
|
/**
|
|
791
|
-
* `
|
|
792
|
-
*
|
|
793
|
-
*
|
|
794
|
-
*
|
|
1134
|
+
* `name` is the skill exactly as posted. `concept` is what thejob-taxonomy-service
|
|
1135
|
+
* resolved it to (`vocabulary: skills`), written by jobs-service, never by the
|
|
1136
|
+
* poster: enough of the concept to filter by id and display the canonical name
|
|
1137
|
+
* without a lookup. A copy, so it can go stale after a taxonomy release; see
|
|
1138
|
+
* `taxonomyVersion`.
|
|
1139
|
+
*
|
|
1140
|
+
* Additive, never authoritative: the raw `name` is always kept, so a wrong
|
|
1141
|
+
* match, a taxonomy release or an outage is recoverable by re-normalizing.
|
|
1142
|
+
* `concept` is absent when the term did not resolve (a normal outcome, logged
|
|
1143
|
+
* as a miss on the taxonomy side) or the job predates normalization, so read
|
|
1144
|
+
* `concept?.name ?? name`.
|
|
795
1145
|
*/
|
|
796
|
-
skills:
|
|
797
|
-
SkillSchema.pick(["name", "logo"
|
|
1146
|
+
skills: array10().of(
|
|
1147
|
+
SkillSchema.pick(["name", "logo"]).shape({
|
|
1148
|
+
concept: ConceptSchema.pick(["conceptId", "vocabulary", "name", "type"]).nullable().optional().default(void 0)
|
|
1149
|
+
}).required().label("Skill")
|
|
798
1150
|
).nullable().optional().label("Skills"),
|
|
799
|
-
bookmarks:
|
|
800
|
-
embedding:
|
|
801
|
-
vector:
|
|
802
|
-
model:
|
|
1151
|
+
bookmarks: array10().of(UserIdAndCreatedAtSchema).optional().label("Bookmarks"),
|
|
1152
|
+
embedding: object19({
|
|
1153
|
+
vector: array10(number7().required()).optional().label("Embedding vector"),
|
|
1154
|
+
model: string15().optional().label("Embedding model")
|
|
803
1155
|
}).nullable().optional().label("Embedding"),
|
|
804
|
-
applicants:
|
|
805
|
-
applicantCount:
|
|
806
|
-
externalApplicationLinkClickCount:
|
|
807
|
-
description:
|
|
808
|
-
descriptionMarkdown:
|
|
809
|
-
descriptionHTML:
|
|
1156
|
+
applicants: array10().of(UserIdAndCreatedAtSchema).optional().label("Applicants"),
|
|
1157
|
+
applicantCount: number7().optional().label("Applicant count"),
|
|
1158
|
+
externalApplicationLinkClickCount: number7().optional().label("External application link click count"),
|
|
1159
|
+
description: string15().required().min(100).max(5e4).label("Description"),
|
|
1160
|
+
descriptionMarkdown: string15().required().label("Description Markdown"),
|
|
1161
|
+
descriptionHTML: string15().optional().label("Description HTML"),
|
|
810
1162
|
/**
|
|
811
1163
|
* Job locations. Required even for remote roles (compliance/legal). Defaults
|
|
812
1164
|
* to an empty array; min(1) is intentionally not enforced yet.
|
|
813
1165
|
*/
|
|
814
|
-
locations:
|
|
815
|
-
educationLevel:
|
|
1166
|
+
locations: array10().of(LocationSchema).default([]).label("Location"),
|
|
1167
|
+
educationLevel: array10(string15().oneOf(SupportedEducationLevels)).required().min(1).label("Educational level"),
|
|
816
1168
|
/** Posting validity window (start/expiry). */
|
|
817
1169
|
validity: DurationSchema({
|
|
818
1170
|
format: SYSTEM_DATE_FORMAT,
|
|
@@ -825,21 +1177,21 @@ var JobSchema = object18({
|
|
|
825
1177
|
* has no `validity.endDate` and must be verified by crawling its page. Guards
|
|
826
1178
|
* re-check cadence so the same job is not re-crawled on every sweep.
|
|
827
1179
|
*/
|
|
828
|
-
lastExpiryCheckAt:
|
|
1180
|
+
lastExpiryCheckAt: number7().optional().nullable().label("Last expiry check at"),
|
|
829
1181
|
/**
|
|
830
1182
|
* When this job transitioned to `status: closed` (epoch ms). Server-owned
|
|
831
1183
|
* operational field: stamped by every jobs-service close path (expiry
|
|
832
1184
|
* auto-close and the dated lapse sweep). Distinct from `updatedAt` so a later
|
|
833
1185
|
* edit to a closed job does not move it.
|
|
834
1186
|
*/
|
|
835
|
-
closedAt:
|
|
836
|
-
experienceLevel:
|
|
837
|
-
contactEmail:
|
|
838
|
-
workingHoursPerWeek:
|
|
839
|
-
tags:
|
|
1187
|
+
closedAt: number7().optional().nullable().label("Closed at"),
|
|
1188
|
+
experienceLevel: string15().oneOf(SupportedExperienceLevels).nullable().optional().label("Experience level"),
|
|
1189
|
+
contactEmail: string15().email().nullable().optional().label("Contact email"),
|
|
1190
|
+
workingHoursPerWeek: number7().nullable().optional().min(1).max(24 * 7).label("Working hours per week"),
|
|
1191
|
+
tags: array10().max(50).optional().label("Tags"),
|
|
840
1192
|
/** Direct/Quick-apply questionnaire. Each item is validated against the
|
|
841
1193
|
* concrete question schema for its `type`, with the stored `answers` omitted. */
|
|
842
|
-
questionnaire:
|
|
1194
|
+
questionnaire: array10().of(
|
|
843
1195
|
lazy((question) => {
|
|
844
1196
|
const schema = getSchemaByQuestion(question.type);
|
|
845
1197
|
if (question.type === "input" /* Input */) {
|
|
@@ -856,12 +1208,12 @@ var JobSchema = object18({
|
|
|
856
1208
|
return schema;
|
|
857
1209
|
})
|
|
858
1210
|
).label("Questionnaire"),
|
|
859
|
-
status:
|
|
860
|
-
seoTags:
|
|
861
|
-
description:
|
|
862
|
-
keywords:
|
|
1211
|
+
status: string15().oneOf(SupportedJobStatuses).default("draft" /* Draft */).required().label("Status"),
|
|
1212
|
+
seoTags: object19({
|
|
1213
|
+
description: string15().optional().label("Description"),
|
|
1214
|
+
keywords: array10().of(string15()).optional().label("Keywords")
|
|
863
1215
|
}).nullable().optional().label("SEO Tags"),
|
|
864
|
-
jdSummary:
|
|
1216
|
+
jdSummary: string15().optional().label("JD Summary"),
|
|
865
1217
|
/**
|
|
866
1218
|
* Structured, model-extracted sections of the posting, re-organized into clean
|
|
867
1219
|
* bullet points. This is our OWN unique presentation of the job (rendered as the
|
|
@@ -870,28 +1222,28 @@ var JobSchema = object18({
|
|
|
870
1222
|
* optional: absent on jobs not yet (re-)enriched, and any section is `[]`/omitted
|
|
871
1223
|
* when the posting genuinely has none.
|
|
872
1224
|
*/
|
|
873
|
-
jobHighlights:
|
|
874
|
-
responsibilities:
|
|
875
|
-
requirements:
|
|
876
|
-
benefits:
|
|
877
|
-
qualifications:
|
|
1225
|
+
jobHighlights: object19({
|
|
1226
|
+
responsibilities: array10().of(string15()).optional().label("Responsibilities"),
|
|
1227
|
+
requirements: array10().of(string15()).optional().label("Requirements"),
|
|
1228
|
+
benefits: array10().of(string15()).optional().label("Benefits"),
|
|
1229
|
+
qualifications: array10().of(string15()).optional().label("Qualifications")
|
|
878
1230
|
}).nullable().optional().label("Job Highlights"),
|
|
879
|
-
canCreateAlert:
|
|
880
|
-
canDirectApply:
|
|
881
|
-
hasApplied:
|
|
882
|
-
isOwner:
|
|
883
|
-
hasBookmarked:
|
|
884
|
-
hasReported:
|
|
885
|
-
ratePerHour:
|
|
886
|
-
isPromoted:
|
|
887
|
-
salaryRange:
|
|
888
|
-
currency:
|
|
889
|
-
min:
|
|
890
|
-
max:
|
|
891
|
-
compensationType:
|
|
892
|
-
isNegotiable:
|
|
1231
|
+
canCreateAlert: boolean12().optional().label("Can create alert"),
|
|
1232
|
+
canDirectApply: boolean12().optional().label("Can direct apply"),
|
|
1233
|
+
hasApplied: boolean12().optional().label("Has applied"),
|
|
1234
|
+
isOwner: boolean12().optional().label("Is owner"),
|
|
1235
|
+
hasBookmarked: boolean12().optional().label("Has bookmarked"),
|
|
1236
|
+
hasReported: boolean12().optional().label("Has reported"),
|
|
1237
|
+
ratePerHour: number7().optional().min(0).label("Rate per hour"),
|
|
1238
|
+
isPromoted: boolean12().optional().label("Is promoted"),
|
|
1239
|
+
salaryRange: object19({
|
|
1240
|
+
currency: string15().required().label("Currency").default("USD"),
|
|
1241
|
+
min: number7().nullable().optional().min(0).label("Minimum Salary"),
|
|
1242
|
+
max: number7().nullable().optional().min(0).label("Maximum Salary"),
|
|
1243
|
+
compensationType: string15().oneOf(SupportedCompensationTypes).nullable().optional().label("Compensation Type"),
|
|
1244
|
+
isNegotiable: boolean12().nullable().optional().default(true).label("Is Negotiable")
|
|
893
1245
|
}).nullable().optional().label("Salary Range"),
|
|
894
|
-
reports:
|
|
1246
|
+
reports: array10().of(UserIdAndCreatedAtSchema).optional().label("Reports"),
|
|
895
1247
|
// Taxonomy fields are constants (see job-taxonomy.constant.ts), stored as slugs and
|
|
896
1248
|
// translated for display. They are deliberately NOT `oneOf`-constrained here: a value
|
|
897
1249
|
// outside the taxonomy must be DROPPED, never a reason to reject the posting. Yup
|
|
@@ -904,17 +1256,25 @@ var JobSchema = object18({
|
|
|
904
1256
|
// time (values get renamed, retired, added), so stored jobs will always outlive the
|
|
905
1257
|
// enum — being lenient here is what lets the taxonomy evolve without a migration
|
|
906
1258
|
// gate on every write.
|
|
907
|
-
category:
|
|
908
|
-
industry:
|
|
909
|
-
subCategories:
|
|
910
|
-
|
|
911
|
-
|
|
1259
|
+
category: string15().nullable().optional().label("Category"),
|
|
1260
|
+
industry: string15().nullable().optional().label("Industry"),
|
|
1261
|
+
subCategories: array10().of(string15().trim().required()).nullable().optional().label("Sub Categories"),
|
|
1262
|
+
/**
|
|
1263
|
+
* The taxonomy state `skills[].concept` was resolved against (the service's
|
|
1264
|
+
* `taxonomyVersion`, bumped by every release and rollback); behind the current
|
|
1265
|
+
* version means the concepts may be stale. Written by jobs-service with every
|
|
1266
|
+
* skills write: `null` when that write could not be normalized (taxonomy down
|
|
1267
|
+
* or unconfigured), absent on jobs that predate normalization.
|
|
1268
|
+
*/
|
|
1269
|
+
taxonomyVersion: number7().integer().min(0).nullable().optional().label("Taxonomy Version"),
|
|
1270
|
+
termsAccepted: boolean12().oneOf([true], "Accept terms before proceeding").required("Accept terms before proceeding").label("Terms accepted"),
|
|
1271
|
+
isFeatured: boolean12().optional().default(false).label("Is featured"),
|
|
912
1272
|
/** Market country codes to feature this job in; empty = global. */
|
|
913
|
-
featuredMarkets:
|
|
1273
|
+
featuredMarkets: array10().of(string15().required()).optional().default([]).label("Featured markets")
|
|
914
1274
|
}).concat(DbDefaultSchema).noUnknown().label("Job");
|
|
915
1275
|
|
|
916
1276
|
// src/job/job-filter.schema.ts
|
|
917
|
-
import { boolean as
|
|
1277
|
+
import { boolean as boolean13, number as number8, object as object20, string as string16 } from "yup";
|
|
918
1278
|
|
|
919
1279
|
// src/user/user.constant.ts
|
|
920
1280
|
var UserRole = /* @__PURE__ */ ((UserRole2) => {
|
|
@@ -2102,7 +2462,7 @@ var TAXONOMY_LABELS = {
|
|
|
2102
2462
|
|
|
2103
2463
|
// src/job/job-filter.schema.ts
|
|
2104
2464
|
var GEO_GROUP = "Geo";
|
|
2105
|
-
var JobFilterShape =
|
|
2465
|
+
var JobFilterShape = object20({
|
|
2106
2466
|
// ── Classification ──────────────────────────────────────────────────────────
|
|
2107
2467
|
// Closed vocabularies, sent verbatim and matched exactly: a partial match
|
|
2108
2468
|
// would make "remote" also match a hypothetical "remote_first".
|
|
@@ -2245,8 +2605,8 @@ var JobFilterShape = object19({
|
|
|
2245
2605
|
// ── Flags ───────────────────────────────────────────────────────────────────
|
|
2246
2606
|
// No `.default(false)`: an unchecked box must mean "no constraint", not "only
|
|
2247
2607
|
// the non-featured ones", so absent simply omits the clause.
|
|
2248
|
-
isFeatured:
|
|
2249
|
-
isPromoted:
|
|
2608
|
+
isFeatured: boolean13().optional().label("Featured Only"),
|
|
2609
|
+
isPromoted: boolean13().optional().label("Promoted Only"),
|
|
2250
2610
|
featuredMarkets: tokens("Featured Markets"),
|
|
2251
2611
|
// ── Ranges ──────────────────────────────────────────────────────────────────
|
|
2252
2612
|
/**
|
|
@@ -2257,10 +2617,10 @@ var JobFilterShape = object19({
|
|
|
2257
2617
|
// can be selected and a bound is meaningless across them. Here the currency is
|
|
2258
2618
|
// a single sibling field, so the bound is plain and the currency simply
|
|
2259
2619
|
// narrows which jobs are compared.
|
|
2260
|
-
salary:
|
|
2261
|
-
min:
|
|
2262
|
-
max:
|
|
2263
|
-
currency:
|
|
2620
|
+
salary: object20({
|
|
2621
|
+
min: number8().integer().min(0).optional().default(void 0).label("Salary \u2265"),
|
|
2622
|
+
max: number8().integer().min(0).optional().default(void 0).label("Salary \u2264"),
|
|
2623
|
+
currency: string16().oneOf(SupportedSalaryCurrencies).optional().label("Currency")
|
|
2264
2624
|
}).optional().default(void 0).label("Salary"),
|
|
2265
2625
|
/** Posted within this many days. Relative, so a saved filter does not rot. */
|
|
2266
2626
|
postedWithinDays: days("Posted Within (days)"),
|
|
@@ -2269,7 +2629,7 @@ var JobFilterShape = object19({
|
|
|
2269
2629
|
* relative `postedWithinDays`. EPOCH MS, matching how the field is stored — a
|
|
2270
2630
|
* coerced date string would not compare.
|
|
2271
2631
|
*/
|
|
2272
|
-
createdAt:
|
|
2632
|
+
createdAt: object20({
|
|
2273
2633
|
min: epoch("Posted After"),
|
|
2274
2634
|
max: epoch("Posted Before")
|
|
2275
2635
|
}).optional().default(void 0).label("Posted Between")
|
|
@@ -2323,7 +2683,7 @@ function deriveIndustry(category, subCategories) {
|
|
|
2323
2683
|
}
|
|
2324
2684
|
|
|
2325
2685
|
// src/post/post.schema.ts
|
|
2326
|
-
import { array as
|
|
2686
|
+
import { array as array12, boolean as boolean14, number as number9, object as object21, string as string17 } from "yup";
|
|
2327
2687
|
|
|
2328
2688
|
// src/post/post.constant.ts
|
|
2329
2689
|
var PostStatus = /* @__PURE__ */ ((PostStatus2) => {
|
|
@@ -2335,41 +2695,41 @@ var PostStatus = /* @__PURE__ */ ((PostStatus2) => {
|
|
|
2335
2695
|
var SupportedPostStatuses = Object.values(PostStatus);
|
|
2336
2696
|
|
|
2337
2697
|
// src/post/post.schema.ts
|
|
2338
|
-
var PostSchema =
|
|
2698
|
+
var PostSchema = object21({
|
|
2339
2699
|
/** Headline shown in listings and at the top of the post. */
|
|
2340
|
-
title:
|
|
2700
|
+
title: string17().required().min(5).max(200).label("Title"),
|
|
2341
2701
|
/** User-friendly, unique URL identifier. */
|
|
2342
|
-
slug:
|
|
2702
|
+
slug: string17().required().max(250).label("Slug"),
|
|
2343
2703
|
/** Short summary used in listings and meta description fallback. */
|
|
2344
|
-
excerpt:
|
|
2704
|
+
excerpt: string17().max(500).optional().label("Excerpt"),
|
|
2345
2705
|
/** Rendered HTML source of truth for the post body (sanitized on write/render).
|
|
2346
2706
|
* Supports rich formatting markdown can't express (image size/alignment, etc). */
|
|
2347
|
-
bodyHTML:
|
|
2707
|
+
bodyHTML: string17().required().min(1).label("Body (HTML)"),
|
|
2348
2708
|
/** Optional Markdown mirror of the body (portable text / search fallback).
|
|
2349
2709
|
* Kept for back-compat and plain-text derivation; bodyHTML is authoritative. */
|
|
2350
|
-
bodyMarkdown:
|
|
2710
|
+
bodyMarkdown: string17().optional().label("Body (Markdown)"),
|
|
2351
2711
|
/** Cover/hero image URL. Not `.url()`-validated: the value is either an
|
|
2352
2712
|
* uploaded file URL served by this platform (which may be a bare host like
|
|
2353
2713
|
* `http://localhost:3082/...` in dev, that yup's `.url()` wrongly rejects) or
|
|
2354
2714
|
* an admin-pasted link. Kept as a plain string; empty ⇒ null. */
|
|
2355
|
-
coverImage:
|
|
2715
|
+
coverImage: string17().nullable().optional().label("Cover image"),
|
|
2356
2716
|
/** Author (user id). */
|
|
2357
|
-
authorId:
|
|
2717
|
+
authorId: string17().required().label("Author ID"),
|
|
2358
2718
|
/** Free-form tags for filtering and related-posts. */
|
|
2359
|
-
tags:
|
|
2719
|
+
tags: array12().of(string17().trim().required()).default([]).label("Tags"),
|
|
2360
2720
|
/** Estimated reading time in minutes (server-derived from body length). */
|
|
2361
|
-
readingTimeMinutes:
|
|
2721
|
+
readingTimeMinutes: number9().min(0).optional().label("Reading time (minutes)"),
|
|
2362
2722
|
/** SEO overrides. */
|
|
2363
|
-
seoTags:
|
|
2364
|
-
description:
|
|
2365
|
-
keywords:
|
|
2723
|
+
seoTags: object21({
|
|
2724
|
+
description: string17().optional().label("Description"),
|
|
2725
|
+
keywords: array12().of(string17().required()).optional().label("Keywords")
|
|
2366
2726
|
}).nullable().optional().label("SEO Tags"),
|
|
2367
2727
|
/** When the post was first published (epoch ms). Server-owned. */
|
|
2368
|
-
publishedAt:
|
|
2728
|
+
publishedAt: number9().nullable().optional().label("Published at"),
|
|
2369
2729
|
/** Whether the post is featured in the blog listing. */
|
|
2370
|
-
isFeatured:
|
|
2730
|
+
isFeatured: boolean14().default(false).optional().label("Is featured"),
|
|
2371
2731
|
/** Publication state. */
|
|
2372
|
-
status:
|
|
2732
|
+
status: string17().oneOf(SupportedPostStatuses).default("draft" /* Draft */).required().label("Status")
|
|
2373
2733
|
}).concat(DbDefaultSchema).noUnknown().label("Post");
|
|
2374
2734
|
|
|
2375
2735
|
// src/geo/geo.constant.ts
|
|
@@ -2419,11 +2779,11 @@ var GEO_COLLECTIONS = {
|
|
|
2419
2779
|
var GEO_SEARCH_MAX = 64;
|
|
2420
2780
|
|
|
2421
2781
|
// src/geo/country.schema.ts
|
|
2422
|
-
import { array as
|
|
2423
|
-
var CountrySchema =
|
|
2782
|
+
import { array as array13, number as number10, object as object22, string as string18 } from "yup";
|
|
2783
|
+
var CountrySchema = object22({
|
|
2424
2784
|
/** The upstream dr5hn id. Stable, and the key every upsert matches on. */
|
|
2425
|
-
id:
|
|
2426
|
-
name:
|
|
2785
|
+
id: number10().integer().required().label("ID"),
|
|
2786
|
+
name: string18().trim().required().label("Name"),
|
|
2427
2787
|
/**
|
|
2428
2788
|
* ISO 3166-1 alpha-2, UPPERCASE (`IN`, `SE`).
|
|
2429
2789
|
*
|
|
@@ -2432,34 +2792,34 @@ var CountrySchema = object21({
|
|
|
2432
2792
|
* already clean uppercase alpha-2. A lowercase value here would compare
|
|
2433
2793
|
* unequal to all of them and silently match nothing.
|
|
2434
2794
|
*/
|
|
2435
|
-
countryCode:
|
|
2436
|
-
iso3:
|
|
2437
|
-
numericCode:
|
|
2438
|
-
phonecode:
|
|
2439
|
-
capital:
|
|
2440
|
-
currency:
|
|
2441
|
-
currencyName:
|
|
2442
|
-
currencySymbol:
|
|
2443
|
-
tld:
|
|
2795
|
+
countryCode: string18().trim().uppercase().length(2).required().label("Country Code"),
|
|
2796
|
+
iso3: string18().trim().uppercase().length(3).optional().nullable().label("ISO3"),
|
|
2797
|
+
numericCode: string18().trim().optional().nullable().label("Numeric Code"),
|
|
2798
|
+
phonecode: string18().trim().optional().nullable().label("Phone Code"),
|
|
2799
|
+
capital: string18().trim().optional().nullable().label("Capital"),
|
|
2800
|
+
currency: string18().trim().optional().nullable().label("Currency"),
|
|
2801
|
+
currencyName: string18().trim().optional().nullable().label("Currency Name"),
|
|
2802
|
+
currencySymbol: string18().trim().optional().nullable().label("Currency Symbol"),
|
|
2803
|
+
tld: string18().trim().optional().nullable().label("TLD"),
|
|
2444
2804
|
/** The name in its own script, e.g. "भारत". Stored, but NOT searchable; see `searchByLocale`. */
|
|
2445
|
-
native:
|
|
2446
|
-
population:
|
|
2447
|
-
gdp:
|
|
2448
|
-
region:
|
|
2449
|
-
regionId:
|
|
2450
|
-
subregion:
|
|
2451
|
-
subregionId:
|
|
2452
|
-
nationality:
|
|
2453
|
-
areaSqKm:
|
|
2454
|
-
postalCodeFormat:
|
|
2455
|
-
postalCodeRegex:
|
|
2456
|
-
timezones:
|
|
2457
|
-
|
|
2458
|
-
zoneName:
|
|
2459
|
-
gmtOffset:
|
|
2460
|
-
gmtOffsetName:
|
|
2461
|
-
abbreviation:
|
|
2462
|
-
tzName:
|
|
2805
|
+
native: string18().trim().optional().nullable().label("Native Name"),
|
|
2806
|
+
population: number10().optional().nullable().label("Population"),
|
|
2807
|
+
gdp: number10().optional().nullable().label("GDP"),
|
|
2808
|
+
region: string18().trim().optional().nullable().label("Region"),
|
|
2809
|
+
regionId: number10().integer().optional().nullable().label("Region ID"),
|
|
2810
|
+
subregion: string18().trim().optional().nullable().label("Subregion"),
|
|
2811
|
+
subregionId: number10().integer().optional().nullable().label("Subregion ID"),
|
|
2812
|
+
nationality: string18().trim().optional().nullable().label("Nationality"),
|
|
2813
|
+
areaSqKm: number10().optional().nullable().label("Area (sq km)"),
|
|
2814
|
+
postalCodeFormat: string18().trim().optional().nullable().label("Postal Code Format"),
|
|
2815
|
+
postalCodeRegex: string18().optional().nullable().label("Postal Code Regex"),
|
|
2816
|
+
timezones: array13().of(
|
|
2817
|
+
object22({
|
|
2818
|
+
zoneName: string18().optional().nullable().label("Zone Name"),
|
|
2819
|
+
gmtOffset: number10().optional().nullable().label("GMT Offset"),
|
|
2820
|
+
gmtOffsetName: string18().optional().nullable().label("GMT Offset Name"),
|
|
2821
|
+
abbreviation: string18().optional().nullable().label("Abbreviation"),
|
|
2822
|
+
tzName: string18().optional().nullable().label("TZ Name")
|
|
2463
2823
|
})
|
|
2464
2824
|
).optional().nullable().label("Timezones"),
|
|
2465
2825
|
/**
|
|
@@ -2468,7 +2828,7 @@ var CountrySchema = object21({
|
|
|
2468
2828
|
* declaring it here would turn a new locale into a validation failure. The
|
|
2469
2829
|
* VALUES are split per-locale into `searchByLocale`.
|
|
2470
2830
|
*/
|
|
2471
|
-
translations:
|
|
2831
|
+
translations: object22().optional().nullable().label("Translations"),
|
|
2472
2832
|
/**
|
|
2473
2833
|
* Coordinates as NUMBERS.
|
|
2474
2834
|
*
|
|
@@ -2477,10 +2837,10 @@ var CountrySchema = object21({
|
|
|
2477
2837
|
* `"9" > "10"` is true, so a bounding-box filter would be quietly wrong rather
|
|
2478
2838
|
* than failing loudly.
|
|
2479
2839
|
*/
|
|
2480
|
-
latitude:
|
|
2481
|
-
longitude:
|
|
2482
|
-
emoji:
|
|
2483
|
-
emojiU:
|
|
2840
|
+
latitude: number10().optional().nullable().label("Latitude"),
|
|
2841
|
+
longitude: number10().optional().nullable().label("Longitude"),
|
|
2842
|
+
emoji: string18().optional().nullable().label("Emoji"),
|
|
2843
|
+
emojiU: string18().optional().nullable().label("Emoji Unicode"),
|
|
2484
2844
|
/**
|
|
2485
2845
|
* The name field searched for the DEFAULT (English) locale: the canonical
|
|
2486
2846
|
* `name`, and nothing else.
|
|
@@ -2490,7 +2850,7 @@ var CountrySchema = object21({
|
|
|
2490
2850
|
* field would make every locale's search implicitly a search of all 19
|
|
2491
2851
|
* languages: a French UI searching "Inde" would also match German "Indien".
|
|
2492
2852
|
*/
|
|
2493
|
-
searchDefault:
|
|
2853
|
+
searchDefault: array13().of(string18().required()).optional().default([]).label("Search Default"),
|
|
2494
2854
|
/**
|
|
2495
2855
|
* Per-locale name fields, keyed by lowercased BCP-47 tag (`de`, `pt-br`).
|
|
2496
2856
|
* Each carries ONLY that locale's translation, which is what makes
|
|
@@ -2508,21 +2868,21 @@ var CountrySchema = object21({
|
|
|
2508
2868
|
* it, zh-CN, tr, ru, uk, pl, hi, ar. **`sv` is absent**, so a Swedish search
|
|
2509
2869
|
* matches nothing. That is the accepted cost of strict scoping.
|
|
2510
2870
|
*/
|
|
2511
|
-
searchByLocale:
|
|
2871
|
+
searchByLocale: object22().optional().nullable().label("Search By Locale"),
|
|
2512
2872
|
/** Epoch ms of the run that last wrote this row. Drives the stale-row prune. */
|
|
2513
|
-
syncedAt:
|
|
2873
|
+
syncedAt: number10().optional().nullable().label("Synced At")
|
|
2514
2874
|
}).label("Country");
|
|
2515
2875
|
|
|
2516
2876
|
// src/geo/state.schema.ts
|
|
2517
|
-
import { array as
|
|
2518
|
-
var StateSchema =
|
|
2877
|
+
import { array as array14, number as number11, object as object23, string as string19 } from "yup";
|
|
2878
|
+
var StateSchema = object23({
|
|
2519
2879
|
/** The upstream dr5hn id. Stable, and the key every upsert matches on. */
|
|
2520
|
-
id:
|
|
2521
|
-
name:
|
|
2522
|
-
countryId:
|
|
2880
|
+
id: number11().integer().required().label("ID"),
|
|
2881
|
+
name: string19().trim().required().label("Name"),
|
|
2882
|
+
countryId: number11().integer().optional().nullable().label("Country ID"),
|
|
2523
2883
|
/** ISO 3166-1 alpha-2 of the parent, UPPERCASE. See `CountrySchema.countryCode`. */
|
|
2524
|
-
countryCode:
|
|
2525
|
-
countryName:
|
|
2884
|
+
countryCode: string19().trim().uppercase().length(2).required().label("Country Code"),
|
|
2885
|
+
countryName: string19().trim().optional().nullable().label("Country Name"),
|
|
2526
2886
|
/**
|
|
2527
2887
|
* The BARE subdivision code, e.g. `KA` (upstream `iso2`).
|
|
2528
2888
|
*
|
|
@@ -2534,22 +2894,22 @@ var StateSchema = object22({
|
|
|
2534
2894
|
* both are stored so the migration has its destination present before any
|
|
2535
2895
|
* consumer moves.
|
|
2536
2896
|
*/
|
|
2537
|
-
stateCode:
|
|
2897
|
+
stateCode: string19().trim().uppercase().optional().nullable().label("State Code"),
|
|
2538
2898
|
/** Fully-qualified ISO 3166-2, e.g. `IN-KA` (upstream `iso3166_2`). */
|
|
2539
|
-
stateCodeIso:
|
|
2540
|
-
fipsCode:
|
|
2899
|
+
stateCodeIso: string19().trim().uppercase().optional().nullable().label("ISO 3166-2 Code"),
|
|
2900
|
+
fipsCode: string19().trim().optional().nullable().label("FIPS Code"),
|
|
2541
2901
|
/** Upstream's own word for the subdivision: "province", "state", "district", … */
|
|
2542
|
-
type:
|
|
2543
|
-
level:
|
|
2544
|
-
parentId:
|
|
2902
|
+
type: string19().trim().optional().nullable().label("Type"),
|
|
2903
|
+
level: number11().integer().optional().nullable().label("Level"),
|
|
2904
|
+
parentId: number11().integer().optional().nullable().label("Parent ID"),
|
|
2545
2905
|
/** The name in its own script. Stored, but NOT searchable; see `searchByLocale`. */
|
|
2546
|
-
native:
|
|
2906
|
+
native: string19().trim().optional().nullable().label("Native Name"),
|
|
2547
2907
|
/** Numbers, not the strings upstream ships. See `CountrySchema.latitude`. */
|
|
2548
|
-
latitude:
|
|
2549
|
-
longitude:
|
|
2550
|
-
timezone:
|
|
2908
|
+
latitude: number11().optional().nullable().label("Latitude"),
|
|
2909
|
+
longitude: number11().optional().nullable().label("Longitude"),
|
|
2910
|
+
timezone: string19().trim().optional().nullable().label("Timezone"),
|
|
2551
2911
|
/** Localized names keyed by locale. Unshaped; see `CountrySchema.translations`. */
|
|
2552
|
-
translations:
|
|
2912
|
+
translations: object23().optional().nullable().label("Translations"),
|
|
2553
2913
|
/**
|
|
2554
2914
|
* The name field searched for the DEFAULT (English) locale: the canonical
|
|
2555
2915
|
* `name`, and nothing else.
|
|
@@ -2559,7 +2919,7 @@ var StateSchema = object22({
|
|
|
2559
2919
|
* field would make every locale's search implicitly a search of all 19
|
|
2560
2920
|
* languages: a French UI searching "Inde" would also match German "Indien".
|
|
2561
2921
|
*/
|
|
2562
|
-
searchDefault:
|
|
2922
|
+
searchDefault: array14().of(string19().required()).optional().default([]).label("Search Default"),
|
|
2563
2923
|
/**
|
|
2564
2924
|
* Per-locale name fields, keyed by lowercased BCP-47 tag (`de`, `pt-br`).
|
|
2565
2925
|
* Each carries ONLY that locale's translation, which is what makes
|
|
@@ -2577,20 +2937,20 @@ var StateSchema = object22({
|
|
|
2577
2937
|
* it, zh-CN, tr, ru, uk, pl, hi, ar. **`sv` is absent**, so a Swedish search
|
|
2578
2938
|
* matches nothing. That is the accepted cost of strict scoping.
|
|
2579
2939
|
*/
|
|
2580
|
-
searchByLocale:
|
|
2940
|
+
searchByLocale: object23().optional().nullable().label("Search By Locale"),
|
|
2581
2941
|
/** Epoch ms of the run that last wrote this row. Drives the stale-row prune. */
|
|
2582
|
-
syncedAt:
|
|
2942
|
+
syncedAt: number11().optional().nullable().label("Synced At")
|
|
2583
2943
|
}).label("State");
|
|
2584
2944
|
|
|
2585
2945
|
// src/geo/city.schema.ts
|
|
2586
|
-
import { array as
|
|
2587
|
-
var CitySchema =
|
|
2946
|
+
import { array as array15, number as number12, object as object24, string as string20 } from "yup";
|
|
2947
|
+
var CitySchema = object24({
|
|
2588
2948
|
/** The upstream dr5hn id. Stable, and the key every upsert matches on. */
|
|
2589
|
-
id:
|
|
2590
|
-
name:
|
|
2591
|
-
stateId:
|
|
2949
|
+
id: number12().integer().required().label("ID"),
|
|
2950
|
+
name: string20().trim().required().label("Name"),
|
|
2951
|
+
stateId: number12().integer().optional().nullable().label("State ID"),
|
|
2592
2952
|
/** The BARE subdivision code. Only meaningful paired with `countryCode`; see `StateSchema`. */
|
|
2593
|
-
stateCode:
|
|
2953
|
+
stateCode: string20().trim().uppercase().optional().nullable().label("State Code"),
|
|
2594
2954
|
/**
|
|
2595
2955
|
* Fully-qualified ISO 3166-2, e.g. `IN-KA`.
|
|
2596
2956
|
*
|
|
@@ -2600,29 +2960,29 @@ var CitySchema = object23({
|
|
|
2600
2960
|
* migration can re-point the cities route without a 153k-row re-ingest.
|
|
2601
2961
|
* Null when either half is missing.
|
|
2602
2962
|
*/
|
|
2603
|
-
stateCodeIso:
|
|
2604
|
-
stateName:
|
|
2605
|
-
countryId:
|
|
2963
|
+
stateCodeIso: string20().trim().uppercase().optional().nullable().label("ISO 3166-2 Code"),
|
|
2964
|
+
stateName: string20().trim().optional().nullable().label("State Name"),
|
|
2965
|
+
countryId: number12().integer().optional().nullable().label("Country ID"),
|
|
2606
2966
|
/** ISO 3166-1 alpha-2 of the parent, UPPERCASE. See `CountrySchema.countryCode`. */
|
|
2607
|
-
countryCode:
|
|
2608
|
-
countryName:
|
|
2967
|
+
countryCode: string20().trim().uppercase().length(2).required().label("Country Code"),
|
|
2968
|
+
countryName: string20().trim().optional().nullable().label("Country Name"),
|
|
2609
2969
|
/** Numbers, not the strings upstream ships. See `CountrySchema.latitude`. */
|
|
2610
|
-
latitude:
|
|
2611
|
-
longitude:
|
|
2970
|
+
latitude: number12().optional().nullable().label("Latitude"),
|
|
2971
|
+
longitude: number12().optional().nullable().label("Longitude"),
|
|
2612
2972
|
/** The name in its own script. Stored, but NOT searchable; see `searchByLocale`. */
|
|
2613
|
-
native:
|
|
2614
|
-
type:
|
|
2615
|
-
level:
|
|
2616
|
-
parentId:
|
|
2973
|
+
native: string20().trim().optional().nullable().label("Native Name"),
|
|
2974
|
+
type: string20().trim().optional().nullable().label("Type"),
|
|
2975
|
+
level: number12().integer().optional().nullable().label("Level"),
|
|
2976
|
+
parentId: number12().integer().optional().nullable().label("Parent ID"),
|
|
2617
2977
|
/**
|
|
2618
2978
|
* Null on most rows. Worth keeping (it is a good relevance signal when
|
|
2619
2979
|
* present) but never worth SORTING by alone, since a null-heavy sort buries
|
|
2620
2980
|
* every city upstream has no figure for.
|
|
2621
2981
|
*/
|
|
2622
|
-
population:
|
|
2623
|
-
timezone:
|
|
2982
|
+
population: number12().optional().nullable().label("Population"),
|
|
2983
|
+
timezone: string20().trim().optional().nullable().label("Timezone"),
|
|
2624
2984
|
/** Localized names keyed by locale. Unshaped; see `CountrySchema.translations`. */
|
|
2625
|
-
translations:
|
|
2985
|
+
translations: object24().optional().nullable().label("Translations"),
|
|
2626
2986
|
/**
|
|
2627
2987
|
* The name field searched for the DEFAULT (English) locale: the canonical
|
|
2628
2988
|
* `name`, and nothing else.
|
|
@@ -2632,7 +2992,7 @@ var CitySchema = object23({
|
|
|
2632
2992
|
* field would make every locale's search implicitly a search of all 19
|
|
2633
2993
|
* languages: a French UI searching "Inde" would also match German "Indien".
|
|
2634
2994
|
*/
|
|
2635
|
-
searchDefault:
|
|
2995
|
+
searchDefault: array15().of(string20().required()).optional().default([]).label("Search Default"),
|
|
2636
2996
|
/**
|
|
2637
2997
|
* Per-locale name fields, keyed by lowercased BCP-47 tag (`de`, `pt-br`).
|
|
2638
2998
|
* Each carries ONLY that locale's translation, which is what makes
|
|
@@ -2650,14 +3010,14 @@ var CitySchema = object23({
|
|
|
2650
3010
|
* it, zh-CN, tr, ru, uk, pl, hi, ar. **`sv` is absent**, so a Swedish search
|
|
2651
3011
|
* matches nothing. That is the accepted cost of strict scoping.
|
|
2652
3012
|
*/
|
|
2653
|
-
searchByLocale:
|
|
3013
|
+
searchByLocale: object24().optional().nullable().label("Search By Locale"),
|
|
2654
3014
|
/** Epoch ms of the run that last wrote this row. Drives the stale-row prune. */
|
|
2655
|
-
syncedAt:
|
|
3015
|
+
syncedAt: number12().optional().nullable().label("Synced At")
|
|
2656
3016
|
}).label("City");
|
|
2657
3017
|
|
|
2658
3018
|
// src/geo/country-filter.schema.ts
|
|
2659
|
-
import { object as
|
|
2660
|
-
var CountryFilterShape =
|
|
3019
|
+
import { object as object25 } from "yup";
|
|
3020
|
+
var CountryFilterShape = object25({
|
|
2661
3021
|
/** ISO 3166-1 alpha-2 codes, e.g. `IN`. Uppercased, matching how they are stored. */
|
|
2662
3022
|
countryCodes: codeTokens("Country Codes"),
|
|
2663
3023
|
/** ISO 3166-1 alpha-3 codes, e.g. `IND`. */
|
|
@@ -2677,8 +3037,8 @@ var CountryFilterSchema = rejectUnknownKeys(
|
|
|
2677
3037
|
);
|
|
2678
3038
|
|
|
2679
3039
|
// src/geo/state-filter.schema.ts
|
|
2680
|
-
import { object as
|
|
2681
|
-
var StateFilterShape =
|
|
3040
|
+
import { object as object26 } from "yup";
|
|
3041
|
+
var StateFilterShape = object26({
|
|
2682
3042
|
/** ISO 3166-1 alpha-2 of the parent country, e.g. `IN`. */
|
|
2683
3043
|
countryCodes: codeTokens("Country Codes"),
|
|
2684
3044
|
/**
|
|
@@ -2708,8 +3068,8 @@ var StateFilterSchema = rejectUnknownKeys(
|
|
|
2708
3068
|
);
|
|
2709
3069
|
|
|
2710
3070
|
// src/geo/city-filter.schema.ts
|
|
2711
|
-
import { object as
|
|
2712
|
-
var CityFilterShape =
|
|
3071
|
+
import { object as object27 } from "yup";
|
|
3072
|
+
var CityFilterShape = object27({
|
|
2713
3073
|
/** ISO 3166-1 alpha-2 of the parent country, e.g. `IN`. */
|
|
2714
3074
|
countryCodes: codeTokens("Country Codes"),
|
|
2715
3075
|
/**
|
|
@@ -2726,28 +3086,28 @@ var CityFilterSchema = rejectUnknownKeys(
|
|
|
2726
3086
|
);
|
|
2727
3087
|
|
|
2728
3088
|
// src/geo/geo-sync.schema.ts
|
|
2729
|
-
import { boolean as
|
|
2730
|
-
var GeoSyncCountsSchema =
|
|
2731
|
-
read:
|
|
2732
|
-
upserted:
|
|
2733
|
-
modified:
|
|
2734
|
-
matched:
|
|
3089
|
+
import { boolean as boolean15, mixed as mixed4, number as number13, object as object28, string as string21 } from "yup";
|
|
3090
|
+
var GeoSyncCountsSchema = object28({
|
|
3091
|
+
read: number13().integer().min(0).default(0).label("Read"),
|
|
3092
|
+
upserted: number13().integer().min(0).default(0).label("Upserted"),
|
|
3093
|
+
modified: number13().integer().min(0).default(0).label("Modified"),
|
|
3094
|
+
matched: number13().integer().min(0).default(0).label("Matched"),
|
|
2735
3095
|
/** Rows removed because upstream dropped them. See `prunedSkipped`. */
|
|
2736
|
-
pruned:
|
|
3096
|
+
pruned: number13().integer().min(0).default(0).label("Pruned"),
|
|
2737
3097
|
/**
|
|
2738
3098
|
* True when the stale-row prune was REFUSED because it would have removed more
|
|
2739
3099
|
* than its safety threshold of the collection. A partial fetch that looked
|
|
2740
3100
|
* like a success must not be able to empty the table, so the run reports the
|
|
2741
3101
|
* refusal rather than acting on it.
|
|
2742
3102
|
*/
|
|
2743
|
-
prunedSkipped:
|
|
3103
|
+
prunedSkipped: boolean15().default(false).label("Prune Skipped")
|
|
2744
3104
|
}).label("Sync Counts");
|
|
2745
|
-
var GeoSyncRunSchema =
|
|
2746
|
-
jobId:
|
|
3105
|
+
var GeoSyncRunSchema = object28({
|
|
3106
|
+
jobId: string21().required().label("Job ID"),
|
|
2747
3107
|
status: mixed4().oneOf(SupportedGeoSyncStatuses).required().label("Status"),
|
|
2748
3108
|
trigger: mixed4().oneOf(SupportedGeoSyncTriggers).optional().label("Trigger"),
|
|
2749
3109
|
/** Whether the caller asked to run even though the release tag was unchanged. */
|
|
2750
|
-
force:
|
|
3110
|
+
force: boolean15().default(false).label("Force"),
|
|
2751
3111
|
/**
|
|
2752
3112
|
* The upstream release tag this run fetched, e.g. `v3.2-export.7`.
|
|
2753
3113
|
*
|
|
@@ -2755,12 +3115,12 @@ var GeoSyncRunSchema = object27({
|
|
|
2755
3115
|
* cannot tell what version it is fetching is otherwise a failure, not a
|
|
2756
3116
|
* refresh, so it is not allowed to proceed unattended.
|
|
2757
3117
|
*/
|
|
2758
|
-
releaseTag:
|
|
3118
|
+
releaseTag: string21().optional().nullable().label("Release Tag"),
|
|
2759
3119
|
/** The tag of the last successful run, i.e. what this one is replacing. */
|
|
2760
|
-
previousReleaseTag:
|
|
2761
|
-
startedAt:
|
|
2762
|
-
finishedAt:
|
|
2763
|
-
durationMs:
|
|
3120
|
+
previousReleaseTag: string21().optional().nullable().label("Previous Release Tag"),
|
|
3121
|
+
startedAt: number13().required().label("Started At"),
|
|
3122
|
+
finishedAt: number13().optional().nullable().label("Finished At"),
|
|
3123
|
+
durationMs: number13().optional().nullable().label("Duration (ms)"),
|
|
2764
3124
|
phase: mixed4().oneOf(SupportedGeoSyncPhases).optional().nullable().label("Phase"),
|
|
2765
3125
|
/**
|
|
2766
3126
|
* Live progress for the dashboard bar.
|
|
@@ -2769,25 +3129,25 @@ var GeoSyncRunSchema = object27({
|
|
|
2769
3129
|
* collection, or a hardcoded fallback on the very first run, since the true
|
|
2770
3130
|
* total is not known until the stream ends. The UI should render it as one.
|
|
2771
3131
|
*/
|
|
2772
|
-
progress:
|
|
2773
|
-
collection:
|
|
2774
|
-
processed:
|
|
2775
|
-
total:
|
|
3132
|
+
progress: object28({
|
|
3133
|
+
collection: string21().optional().nullable().label("Collection"),
|
|
3134
|
+
processed: number13().integer().min(0).default(0).label("Processed"),
|
|
3135
|
+
total: number13().integer().min(0).optional().nullable().label("Total")
|
|
2776
3136
|
}).optional().nullable().label("Progress"),
|
|
2777
|
-
counts:
|
|
3137
|
+
counts: object28({
|
|
2778
3138
|
countries: GeoSyncCountsSchema.optional().nullable().label("Countries"),
|
|
2779
3139
|
states: GeoSyncCountsSchema.optional().nullable().label("States"),
|
|
2780
3140
|
cities: GeoSyncCountsSchema.optional().nullable().label("Cities")
|
|
2781
3141
|
}).optional().nullable().label("Counts"),
|
|
2782
3142
|
/** Why a `skipped` run was skipped, in words the dashboard can show verbatim. */
|
|
2783
|
-
reason:
|
|
3143
|
+
reason: string21().optional().nullable().label("Reason"),
|
|
2784
3144
|
/** The failure message on a `failed` run. Surfaced verbatim to the admin. */
|
|
2785
|
-
error:
|
|
3145
|
+
error: string21().optional().nullable().label("Error")
|
|
2786
3146
|
}).label("Geo Sync Run");
|
|
2787
3147
|
|
|
2788
3148
|
// src/language/language-filter.schema.ts
|
|
2789
|
-
import { object as
|
|
2790
|
-
var LanguageFilterShape =
|
|
3149
|
+
import { object as object29 } from "yup";
|
|
3150
|
+
var LanguageFilterShape = object29({
|
|
2791
3151
|
/** Language names, matched exactly. Free-text matching is `?search=`. */
|
|
2792
3152
|
names: tokens("Names")
|
|
2793
3153
|
}).label("Language Filter");
|
|
@@ -2797,8 +3157,8 @@ var LanguageFilterSchema = rejectUnknownKeys(
|
|
|
2797
3157
|
);
|
|
2798
3158
|
|
|
2799
3159
|
// src/page/page-filter.schema.ts
|
|
2800
|
-
import { boolean as
|
|
2801
|
-
var PageFilterShape =
|
|
3160
|
+
import { boolean as boolean16, object as object30 } from "yup";
|
|
3161
|
+
var PageFilterShape = object30({
|
|
2802
3162
|
/**
|
|
2803
3163
|
* `type`, NOT `pageType`.
|
|
2804
3164
|
*
|
|
@@ -2818,7 +3178,7 @@ var PageFilterShape = object29({
|
|
|
2818
3178
|
// No `.default(false)`: absent means no constraint. Note `isFeatured` is
|
|
2819
3179
|
// present on only 55 documents, so combining it with `status: ['published']`
|
|
2820
3180
|
// leaves 11 pages — a legitimate filter that returns almost nothing.
|
|
2821
|
-
isFeatured:
|
|
3181
|
+
isFeatured: boolean16().optional().label("Featured Only"),
|
|
2822
3182
|
/** Lowercase (`in`, `se`, `ww`), like groups. See `GroupFilterSchema`. */
|
|
2823
3183
|
featuredMarkets: tokens("Featured Markets"),
|
|
2824
3184
|
/** Page SLUGS. A company is matched by slug: a display name breaks on rename. */
|
|
@@ -2831,7 +3191,7 @@ var PageFilterSchema = rejectUnknownKeys(
|
|
|
2831
3191
|
);
|
|
2832
3192
|
|
|
2833
3193
|
// src/marketing/marketing.schema.ts
|
|
2834
|
-
import { array as
|
|
3194
|
+
import { array as array17, boolean as boolean18, lazy as lazy2, mixed as mixed5, number as number15, object as object32, string as string23 } from "yup";
|
|
2835
3195
|
|
|
2836
3196
|
// src/marketing/marketing.constant.ts
|
|
2837
3197
|
var CampaignStatus = /* @__PURE__ */ ((CampaignStatus2) => {
|
|
@@ -2908,8 +3268,8 @@ var SupportedOccurrenceStatuses = Object.values(OccurrenceStatus);
|
|
|
2908
3268
|
var CAMPAIGN_MAX_RECIPIENTS_CEILING = 5e3;
|
|
2909
3269
|
|
|
2910
3270
|
// src/user/user-filter.schema.ts
|
|
2911
|
-
import { array as
|
|
2912
|
-
var UserFilterBranchShape =
|
|
3271
|
+
import { array as array16, boolean as boolean17, number as number14, object as object31, string as string22 } from "yup";
|
|
3272
|
+
var UserFilterBranchShape = object31({
|
|
2913
3273
|
/**
|
|
2914
3274
|
* Exact email addresses. An IDENTITY lookup, not a search.
|
|
2915
3275
|
*
|
|
@@ -2929,7 +3289,7 @@ var UserFilterBranchShape = object30({
|
|
|
2929
3289
|
* the input keeps the match an indexed equality instead of a regex that would
|
|
2930
3290
|
* put the scan straight back.
|
|
2931
3291
|
*/
|
|
2932
|
-
emails:
|
|
3292
|
+
emails: array16().of(string22().required()).optional().default([]).label("Email Addresses"),
|
|
2933
3293
|
/**
|
|
2934
3294
|
* Better Auth account ids. An IDENTITY lookup like `emails`, and for the same
|
|
2935
3295
|
* reason: the caller already knows the exact value.
|
|
@@ -2948,9 +3308,9 @@ var UserFilterBranchShape = object30({
|
|
|
2948
3308
|
* The admin filter panel builds its controls explicitly rather than from this
|
|
2949
3309
|
* shape, so declaring it here does not put an account-id box in the UI.
|
|
2950
3310
|
*/
|
|
2951
|
-
authAccountIds:
|
|
3311
|
+
authAccountIds: array16().of(string22().required()).optional().default([]).label("Auth Account IDs"),
|
|
2952
3312
|
// ── Enum facets (closed vocabularies, chip pickers) ─────────────────────────
|
|
2953
|
-
countries:
|
|
3313
|
+
countries: array16().of(string22().required()).optional().default([]).label("Countries"),
|
|
2954
3314
|
roles: enumFacet(SupportedUserRoles, "Roles"),
|
|
2955
3315
|
// The user's own current seniority (UserSchema.experienceLevel).
|
|
2956
3316
|
experienceLevels: enumFacet(SupportedExperienceLevels, "Experience Levels"),
|
|
@@ -3083,12 +3443,12 @@ var UserFilterBranchShape = object30({
|
|
|
3083
3443
|
// it, so the dependency is stated rather than assumed.
|
|
3084
3444
|
minSalaryFloor: currencyBound("Min Salary \u2265", "salaryCurrencies"),
|
|
3085
3445
|
minSalaryCeil: currencyBound("Min Salary \u2264", "salaryCurrencies"),
|
|
3086
|
-
openToWorkOnly:
|
|
3087
|
-
remoteExperienceOnly:
|
|
3088
|
-
hasResumeOnly:
|
|
3089
|
-
onboardingCompletedOnly:
|
|
3090
|
-
emailVerifiedOnly:
|
|
3091
|
-
mobileVerifiedOnly:
|
|
3446
|
+
openToWorkOnly: boolean17().optional().default(false).label("Open to Work Only"),
|
|
3447
|
+
remoteExperienceOnly: boolean17().optional().default(false).label("Has Remote Experience"),
|
|
3448
|
+
hasResumeOnly: boolean17().optional().default(false).label("Has Resume"),
|
|
3449
|
+
onboardingCompletedOnly: boolean17().optional().default(false).label("Onboarding Completed"),
|
|
3450
|
+
emailVerifiedOnly: boolean17().optional().default(false).label("Email Verified Only"),
|
|
3451
|
+
mobileVerifiedOnly: boolean17().optional().default(false).label("Mobile Verified Only"),
|
|
3092
3452
|
// ── Lifecycle windows ───────────────────────────────────────────────────────
|
|
3093
3453
|
// Relative, because a saved list is resolved fresh on every send: a stored
|
|
3094
3454
|
// absolute epoch would freeze at authoring time and the audience would decay
|
|
@@ -3116,10 +3476,10 @@ var UserFilterBranchShape = object30({
|
|
|
3116
3476
|
// Radius search around a point, against location.geo. All three required
|
|
3117
3477
|
// together; validated as a group rather than field-by-field so a half-filled
|
|
3118
3478
|
// radius cannot silently match everyone.
|
|
3119
|
-
withinRadius:
|
|
3120
|
-
lat:
|
|
3121
|
-
lng:
|
|
3122
|
-
km:
|
|
3479
|
+
withinRadius: object31({
|
|
3480
|
+
lat: number14().min(-90).max(90).required().label("Latitude"),
|
|
3481
|
+
lng: number14().min(-180).max(180).required().label("Longitude"),
|
|
3482
|
+
km: number14().moreThan(0).max(2e4).required().label("Radius (km)")
|
|
3123
3483
|
}).optional().default(void 0).label("Within Radius"),
|
|
3124
3484
|
// ── Signup context (immutable market / locale) ──────────────────────────────
|
|
3125
3485
|
signupCountries: tokens("Signup Countries"),
|
|
@@ -3142,7 +3502,7 @@ var UserFilterBranchShape = object30({
|
|
|
3142
3502
|
// materialise the second case on every list that never chose it.
|
|
3143
3503
|
//
|
|
3144
3504
|
// @todo CONSENT-FILTER-ENFORCEMENT — nothing enforces consent anywhere now.
|
|
3145
|
-
marketingSubscribed:
|
|
3505
|
+
marketingSubscribed: boolean17().optional().label("Marketing Subscribed"),
|
|
3146
3506
|
jobAlertFrequencies: enumFacet(
|
|
3147
3507
|
SupportedJobAlertFrequencies,
|
|
3148
3508
|
"Job Alert Frequency"
|
|
@@ -3163,7 +3523,7 @@ var UserFilterBranchShape = object30({
|
|
|
3163
3523
|
),
|
|
3164
3524
|
// ── Tenure / recency ────────────────────────────────────────────────────────
|
|
3165
3525
|
// Against workExperiences[].duration and educations[].duration ("YYYY-MM").
|
|
3166
|
-
currentlyEmployed:
|
|
3526
|
+
currentlyEmployed: boolean17().optional().default(false).label("Currently Employed"),
|
|
3167
3527
|
graduatedWithinMonths: months("Graduated Within (months)"),
|
|
3168
3528
|
skillUsedWithinMonths: months("Skill Used Within (months)"),
|
|
3169
3529
|
// The ABSOLUTE bounds the three relative windows above resolve into
|
|
@@ -3176,13 +3536,13 @@ var UserFilterBranchShape = object30({
|
|
|
3176
3536
|
// meaning that forever, while "within 18 months" keeps meaning what the admin
|
|
3177
3537
|
// intended. They are declared because the vocabulary must be complete, not
|
|
3178
3538
|
// because a list should store one.
|
|
3179
|
-
graduatedAfter:
|
|
3180
|
-
skillUsedAfter:
|
|
3539
|
+
graduatedAfter: string22().trim().optional().label("Graduated After"),
|
|
3540
|
+
skillUsedAfter: string22().trim().optional().label("Skill Used After"),
|
|
3181
3541
|
// ── Profile richness ────────────────────────────────────────────────────────
|
|
3182
|
-
hasImage:
|
|
3183
|
-
hasHeadline:
|
|
3184
|
-
hasAboutMe:
|
|
3185
|
-
hasMobile:
|
|
3542
|
+
hasImage: boolean17().optional().default(false).label("Has Photo"),
|
|
3543
|
+
hasHeadline: boolean17().optional().default(false).label("Has Headline"),
|
|
3544
|
+
hasAboutMe: boolean17().optional().default(false).label("Has About Me"),
|
|
3545
|
+
hasMobile: boolean17().optional().default(false).label("Has Mobile Number"),
|
|
3186
3546
|
// Segment by WHICH profile section is missing (profileCompleteness.missing[]),
|
|
3187
3547
|
// rather than only by the overall percentage.
|
|
3188
3548
|
missingSections: tokens("Missing Profile Sections"),
|
|
@@ -3194,8 +3554,8 @@ var UserFilterBranchShape = object30({
|
|
|
3194
3554
|
// data lives in other services' collections. Tri-state: `false` is a real
|
|
3195
3555
|
// filter ("has NO job alert"), which is the audience an alert nudge wants, so
|
|
3196
3556
|
// these are `undefined` when unset rather than defaulting to false.
|
|
3197
|
-
hasJobAlert:
|
|
3198
|
-
hasApplied:
|
|
3557
|
+
hasJobAlert: boolean17().optional().default(void 0).label("Has a Job Alert"),
|
|
3558
|
+
hasApplied: boolean17().optional().default(void 0).label("Has Applied"),
|
|
3199
3559
|
appliedWithinDays: days("Applied Within (days)")
|
|
3200
3560
|
// `search` is NOT here. It is free text over name/email, not a facet: it names
|
|
3201
3561
|
// no field on a user document, user-service treats it as a RESERVED PARAM
|
|
@@ -3208,25 +3568,25 @@ var BRANCH_KEYS = Object.keys(UserFilterBranchShape.fields);
|
|
|
3208
3568
|
var UserFilterBranch = rejectUnknownKeys(UserFilterBranchShape, () => BRANCH_KEYS);
|
|
3209
3569
|
var UserFilterSchema = rejectUnknownKeys(
|
|
3210
3570
|
UserFilterBranchShape.shape({
|
|
3211
|
-
or:
|
|
3571
|
+
or: array16().of(UserFilterBranch).optional().default([]).label("Or Any Of")
|
|
3212
3572
|
}).label("User Filter"),
|
|
3213
3573
|
() => [...BRANCH_KEYS, "or"]
|
|
3214
3574
|
);
|
|
3215
3575
|
|
|
3216
3576
|
// src/marketing/marketing.schema.ts
|
|
3217
3577
|
var MailingListFilterSchema = UserFilterSchema;
|
|
3218
|
-
var MailingListSchema =
|
|
3219
|
-
name:
|
|
3220
|
-
description:
|
|
3578
|
+
var MailingListSchema = object32({
|
|
3579
|
+
name: string23().required().min(1).max(120).label("Name"),
|
|
3580
|
+
description: string23().optional().max(500).label("Description"),
|
|
3221
3581
|
filter: MailingListFilterSchema.default(
|
|
3222
3582
|
() => MailingListFilterSchema.getDefault()
|
|
3223
3583
|
)
|
|
3224
3584
|
}).label("Mailing List");
|
|
3225
|
-
var CampaignTriggerSchema =
|
|
3226
|
-
kind:
|
|
3585
|
+
var CampaignTriggerSchema = object32({
|
|
3586
|
+
kind: string23().oneOf(SupportedCampaignTriggerKinds).required().default("manual" /* Manual */).label("Trigger"),
|
|
3227
3587
|
// IANA zone, e.g. "Asia/Kolkata". Local wall-clock times are interpreted in it,
|
|
3228
3588
|
// so occurrences land at the intended hour across DST rather than drifting.
|
|
3229
|
-
timezone:
|
|
3589
|
+
timezone: string23().optional().default("UTC").label("Timezone").when("kind", {
|
|
3230
3590
|
is: (k) => k !== "manual" /* Manual */,
|
|
3231
3591
|
then: (s) => s.required()
|
|
3232
3592
|
}),
|
|
@@ -3240,26 +3600,26 @@ var CampaignTriggerSchema = object31({
|
|
|
3240
3600
|
is: "schedule" /* Schedule */,
|
|
3241
3601
|
then: (s) => s.required()
|
|
3242
3602
|
}),
|
|
3243
|
-
interval:
|
|
3244
|
-
every:
|
|
3245
|
-
unit:
|
|
3603
|
+
interval: object32({
|
|
3604
|
+
every: number15().integer().min(1).max(365).required().label("Every"),
|
|
3605
|
+
unit: string23().oneOf(SupportedCampaignIntervalUnits).required().label("Unit")
|
|
3246
3606
|
}).optional().default(void 0).label("Repeats").when("kind", {
|
|
3247
3607
|
is: "schedule" /* Schedule */,
|
|
3248
3608
|
then: (s) => s.required()
|
|
3249
3609
|
}),
|
|
3250
3610
|
/** Local wall-clock send time, "HH:mm" in `timezone`. */
|
|
3251
|
-
timeOfDay:
|
|
3611
|
+
timeOfDay: string23().matches(/^([01]\d|2[0-3]):[0-5]\d$/, "Use HH:mm").optional().default("09:00").label("Time of Day"),
|
|
3252
3612
|
/** For weekly intervals: ISO weekdays, 1 = Monday .. 7 = Sunday. */
|
|
3253
|
-
byWeekday:
|
|
3613
|
+
byWeekday: array17().of(number15().integer().min(1).max(7).required()).optional().default([]).label("On Days"),
|
|
3254
3614
|
/**
|
|
3255
3615
|
* For monthly intervals: 1-31, or -1 meaning "last day of the month". A month
|
|
3256
3616
|
* shorter than the chosen day is clamped to its last day, so 31 in February
|
|
3257
3617
|
* fires on the 28th/29th rather than being skipped.
|
|
3258
3618
|
*/
|
|
3259
|
-
byMonthday:
|
|
3260
|
-
end:
|
|
3261
|
-
kind:
|
|
3262
|
-
occurrences:
|
|
3619
|
+
byMonthday: number15().integer().min(-1).max(31).notOneOf([0]).optional().default(void 0).label("Day of Month"),
|
|
3620
|
+
end: object32({
|
|
3621
|
+
kind: string23().oneOf(SupportedCampaignEndKinds).required().label("Ends"),
|
|
3622
|
+
occurrences: number15().integer().min(1).max(1e4).optional().default(void 0).label("After N Sends").when("kind", {
|
|
3263
3623
|
is: "afterOccurrences" /* AfterOccurrences */,
|
|
3264
3624
|
then: (s) => s.required()
|
|
3265
3625
|
}),
|
|
@@ -3269,8 +3629,8 @@ var CampaignTriggerSchema = object31({
|
|
|
3269
3629
|
})
|
|
3270
3630
|
}).optional().default(() => ({ kind: "never" /* Never */ })).label("End Condition")
|
|
3271
3631
|
}).label("Trigger");
|
|
3272
|
-
var CampaignDataSourceSchema =
|
|
3273
|
-
id:
|
|
3632
|
+
var CampaignDataSourceSchema = object32({
|
|
3633
|
+
id: string23().required().label("Data Source"),
|
|
3274
3634
|
/**
|
|
3275
3635
|
* Derive this source's filter per recipient from their profile, instead of
|
|
3276
3636
|
* using the admin-entered `params` for everyone.
|
|
@@ -3285,7 +3645,7 @@ var CampaignDataSourceSchema = object31({
|
|
|
3285
3645
|
* un-personalised. Whether a source can honour it at all is declared by the
|
|
3286
3646
|
* source itself (`supportsPersonalisation`), which this package cannot see.
|
|
3287
3647
|
*/
|
|
3288
|
-
personalised:
|
|
3648
|
+
personalised: boolean18().optional().label("Personalised"),
|
|
3289
3649
|
/**
|
|
3290
3650
|
* A free-form bag whose keys are defined by the chosen source's OWN
|
|
3291
3651
|
* `paramsSchema`, which this package cannot see. Same `lazy` treatment as
|
|
@@ -3300,20 +3660,20 @@ var CampaignDataSourceSchema = object31({
|
|
|
3300
3660
|
*/
|
|
3301
3661
|
params: lazy2((value) => {
|
|
3302
3662
|
const keys = value && typeof value === "object" ? Object.keys(value) : [];
|
|
3303
|
-
return
|
|
3663
|
+
return object32(
|
|
3304
3664
|
Object.fromEntries(keys.map((k) => [k, mixed5().optional()]))
|
|
3305
3665
|
).optional().default(() => ({})).label("Parameters");
|
|
3306
3666
|
})
|
|
3307
3667
|
}).label("Data Source");
|
|
3308
|
-
var TemplateVariableBindingSchema =
|
|
3309
|
-
kind:
|
|
3310
|
-
value:
|
|
3668
|
+
var TemplateVariableBindingSchema = object32({
|
|
3669
|
+
kind: string23().oneOf(SupportedTemplateVariableBindingKinds).required().label("Source"),
|
|
3670
|
+
value: string23().required().min(1).max(2e3).label("Value")
|
|
3311
3671
|
}).label("Template Variable Binding");
|
|
3312
|
-
var CampaignSchema =
|
|
3313
|
-
name:
|
|
3672
|
+
var CampaignSchema = object32({
|
|
3673
|
+
name: string23().required().min(1).max(200).label("Campaign Name"),
|
|
3314
3674
|
// The template renders its own subject; an override is only needed for the
|
|
3315
3675
|
// inline-HTML path, hence not required when a template is chosen.
|
|
3316
|
-
subject:
|
|
3676
|
+
subject: string23().optional().max(255).label("Subject").when("templateKey", {
|
|
3317
3677
|
is: (v) => !v,
|
|
3318
3678
|
then: (s) => s.required().min(1)
|
|
3319
3679
|
}),
|
|
@@ -3324,12 +3684,12 @@ var CampaignSchema = object31({
|
|
|
3324
3684
|
* one with a compliance-checked footer and a render test,
|
|
3325
3685
|
* and hand-pasted HTML that repeats forever is a mistake that repeats forever.
|
|
3326
3686
|
*/
|
|
3327
|
-
html:
|
|
3687
|
+
html: string23().optional().label("HTML Body").when("templateKey", {
|
|
3328
3688
|
is: (v) => !v,
|
|
3329
3689
|
then: (s) => s.required().min(1)
|
|
3330
3690
|
}),
|
|
3331
|
-
text:
|
|
3332
|
-
listId:
|
|
3691
|
+
text: string23().optional().label("Plain-text Body"),
|
|
3692
|
+
listId: string23().required().label("Mailing List"),
|
|
3333
3693
|
/**
|
|
3334
3694
|
* Where the campaign is in its life. See `CampaignStatus` for the transitions.
|
|
3335
3695
|
*
|
|
@@ -3338,9 +3698,9 @@ var CampaignSchema = object31({
|
|
|
3338
3698
|
* value the enum never contained (`scheduled`) reached production rows and
|
|
3339
3699
|
* rendered in the admin list as though it were real.
|
|
3340
3700
|
*/
|
|
3341
|
-
status:
|
|
3701
|
+
status: string23().oneOf(SupportedCampaignStatuses).optional().default("draft" /* Draft */).label("Status"),
|
|
3342
3702
|
/** Key from email-service's template registry. Mutually exclusive with `html`. */
|
|
3343
|
-
templateKey:
|
|
3703
|
+
templateKey: string23().optional().label("Email Template"),
|
|
3344
3704
|
/**
|
|
3345
3705
|
* One entry per variable the chosen template declares. Validated at save time
|
|
3346
3706
|
* against the template's own `variables`, so a missing required binding is a
|
|
@@ -3357,7 +3717,7 @@ var CampaignSchema = object31({
|
|
|
3357
3717
|
*/
|
|
3358
3718
|
bindings: lazy2((value) => {
|
|
3359
3719
|
const keys = value && typeof value === "object" ? Object.keys(value) : [];
|
|
3360
|
-
return
|
|
3720
|
+
return object32(
|
|
3361
3721
|
Object.fromEntries(keys.map((k) => [k, TemplateVariableBindingSchema.required()]))
|
|
3362
3722
|
).optional().default(() => ({})).label("Template Data");
|
|
3363
3723
|
}),
|
|
@@ -3369,7 +3729,7 @@ var CampaignSchema = object31({
|
|
|
3369
3729
|
* `groups.groupCount`), so two sources providing the same name cannot
|
|
3370
3730
|
* overwrite each other and a template says which one it means.
|
|
3371
3731
|
*/
|
|
3372
|
-
dataSources:
|
|
3732
|
+
dataSources: array17().of(CampaignDataSourceSchema).optional().default(void 0).label("Data Sources"),
|
|
3373
3733
|
trigger: CampaignTriggerSchema.default(
|
|
3374
3734
|
() => CampaignTriggerSchema.getDefault()
|
|
3375
3735
|
),
|
|
@@ -3384,7 +3744,7 @@ var CampaignSchema = object31({
|
|
|
3384
3744
|
* representable, and the admin list showed exactly that. Activation is a
|
|
3385
3745
|
* state transition, so it belongs in the state.
|
|
3386
3746
|
*/
|
|
3387
|
-
dryRun:
|
|
3747
|
+
dryRun: boolean18().optional().default(true).label("Dry Run"),
|
|
3388
3748
|
/**
|
|
3389
3749
|
* Re-eligibility: when someone this campaign already mailed may get it again.
|
|
3390
3750
|
*
|
|
@@ -3392,23 +3752,23 @@ var CampaignSchema = object31({
|
|
|
3392
3752
|
* person. `N`: again only after N days. Opt-in, because most recurring
|
|
3393
3753
|
* campaigns exist to mail the same people repeatedly.
|
|
3394
3754
|
*/
|
|
3395
|
-
resendAfterDays:
|
|
3396
|
-
maxRecipients:
|
|
3397
|
-
utm:
|
|
3398
|
-
campaign:
|
|
3399
|
-
content:
|
|
3755
|
+
resendAfterDays: number15().integer().min(0).max(365).nullable().optional().default(void 0).label("Resend After (days)"),
|
|
3756
|
+
maxRecipients: number15().integer().min(1).max(CAMPAIGN_MAX_RECIPIENTS_CEILING).optional().default(void 0).label("Max Recipients"),
|
|
3757
|
+
utm: object32({
|
|
3758
|
+
campaign: string23().optional().max(120).label("utm_campaign"),
|
|
3759
|
+
content: string23().optional().max(120).label("utm_content")
|
|
3400
3760
|
}).optional().default(void 0).label("Link Tagging")
|
|
3401
3761
|
}).label("Campaign");
|
|
3402
3762
|
|
|
3403
3763
|
// src/pagination/pagination.schema.ts
|
|
3404
|
-
import { number as
|
|
3405
|
-
var PaginationSchema =
|
|
3406
|
-
page:
|
|
3407
|
-
limit:
|
|
3408
|
-
sortBy:
|
|
3409
|
-
nextPage:
|
|
3410
|
-
prevPage:
|
|
3411
|
-
sortDirection:
|
|
3764
|
+
import { number as number16, object as object33, string as string24 } from "yup";
|
|
3765
|
+
var PaginationSchema = object33().shape({
|
|
3766
|
+
page: number16().required().min(1).label("Page").default(1),
|
|
3767
|
+
limit: number16().required().min(1).max(100).label("Limit").default(10),
|
|
3768
|
+
sortBy: string24().notOneOf([""]).optional().label("SortBy"),
|
|
3769
|
+
nextPage: string24().optional().label("Next Page Token"),
|
|
3770
|
+
prevPage: string24().optional().label("Previous Page Token"),
|
|
3771
|
+
sortDirection: string24().oneOf(["asc", "desc"]).optional().label("Sort Direction")
|
|
3412
3772
|
});
|
|
3413
3773
|
|
|
3414
3774
|
// src/pagination/pagination.constant.ts
|
|
@@ -3424,7 +3784,7 @@ var DefaultPaginationOptions = {
|
|
|
3424
3784
|
};
|
|
3425
3785
|
|
|
3426
3786
|
// src/saved-search/saved-search.schema.ts
|
|
3427
|
-
import { number as
|
|
3787
|
+
import { number as number17, object as object34, string as string25 } from "yup";
|
|
3428
3788
|
|
|
3429
3789
|
// src/saved-search/saved-search.constant.ts
|
|
3430
3790
|
var SavedSearchStatus = /* @__PURE__ */ ((SavedSearchStatus2) => {
|
|
@@ -3435,21 +3795,21 @@ var SavedSearchStatus = /* @__PURE__ */ ((SavedSearchStatus2) => {
|
|
|
3435
3795
|
var SupportedSavedSearchStatuses = Object.values(SavedSearchStatus);
|
|
3436
3796
|
|
|
3437
3797
|
// src/saved-search/saved-search.schema.ts
|
|
3438
|
-
var SavedSearchSchema =
|
|
3798
|
+
var SavedSearchSchema = object34({
|
|
3439
3799
|
/** Public identifier. Server-assigned. */
|
|
3440
|
-
shortId:
|
|
3800
|
+
shortId: string25().trim().label("Short Id"),
|
|
3441
3801
|
/**
|
|
3442
3802
|
* Owner's Better Auth account id. Server-assigned from the verified token,
|
|
3443
3803
|
* never accepted from a request body.
|
|
3444
3804
|
*/
|
|
3445
|
-
ownerUserId:
|
|
3805
|
+
ownerUserId: string25().trim().label("Owner"),
|
|
3446
3806
|
/** What the user calls this search, e.g. "Remote React roles". */
|
|
3447
|
-
label:
|
|
3807
|
+
label: string25().trim().max(120).required().label("Label"),
|
|
3448
3808
|
/**
|
|
3449
3809
|
* The free-text query. Optional because a search can be filters-only ("all
|
|
3450
3810
|
* remote jobs in Sweden"), which is a legitimate alert to want.
|
|
3451
3811
|
*/
|
|
3452
|
-
search:
|
|
3812
|
+
search: string25().trim().max(500).optional().label("Search Text"),
|
|
3453
3813
|
/**
|
|
3454
3814
|
* Field filters, in the same `{ field: values }` shape the search endpoint
|
|
3455
3815
|
* parses from `filter[field]=a,b`. Held as a free-form object rather than a
|
|
@@ -3457,7 +3817,7 @@ var SavedSearchSchema = object33({
|
|
|
3457
3817
|
* change independently; validating them here would put this package in the
|
|
3458
3818
|
* business of tracking that vocabulary.
|
|
3459
3819
|
*/
|
|
3460
|
-
filter:
|
|
3820
|
+
filter: object34().optional().default({}).label("Filters"),
|
|
3461
3821
|
/**
|
|
3462
3822
|
* How often to mail matches. `off` keeps the search saved but silent, which is
|
|
3463
3823
|
* the difference between "I don't want mail" and "delete my search".
|
|
@@ -3466,26 +3826,26 @@ var SavedSearchSchema = object33({
|
|
|
3466
3826
|
* than defining a second one: two vocabularies for the same concept is how
|
|
3467
3827
|
* they drift.
|
|
3468
3828
|
*/
|
|
3469
|
-
frequency:
|
|
3470
|
-
status:
|
|
3829
|
+
frequency: string25().oneOf(SupportedJobAlertFrequencies).default("off" /* Off */).label("Alert Frequency"),
|
|
3830
|
+
status: string25().oneOf(SupportedSavedSearchStatuses).default("active" /* Active */).label("Status"),
|
|
3471
3831
|
/**
|
|
3472
3832
|
* Epoch ms of the last alert sent for this search. The matching sweep reads it
|
|
3473
3833
|
* to decide whether the cadence is due, and writes it only AFTER a successful
|
|
3474
3834
|
* publish, so a failed run retries next tick rather than silently skipping a
|
|
3475
3835
|
* period.
|
|
3476
3836
|
*/
|
|
3477
|
-
lastNotifiedAt:
|
|
3837
|
+
lastNotifiedAt: number17().optional().label("Last Notified At"),
|
|
3478
3838
|
/**
|
|
3479
3839
|
* Epoch ms high-water mark: only jobs created after this are "new" for the
|
|
3480
3840
|
* next alert. Kept separate from `lastNotifiedAt` because a run that finds no
|
|
3481
3841
|
* matches should still advance what counts as new, without implying mail was
|
|
3482
3842
|
* sent.
|
|
3483
3843
|
*/
|
|
3484
|
-
lastMatchedAt:
|
|
3844
|
+
lastMatchedAt: number17().optional().label("Last Matched At")
|
|
3485
3845
|
}).noUnknown().strict();
|
|
3486
3846
|
|
|
3487
3847
|
// src/report/report.schema.ts
|
|
3488
|
-
import { mixed as mixed6, object as
|
|
3848
|
+
import { mixed as mixed6, object as object35, string as string26 } from "yup";
|
|
3489
3849
|
|
|
3490
3850
|
// src/report/report.constant.ts
|
|
3491
3851
|
var ResourceType = /* @__PURE__ */ ((ResourceType3) => {
|
|
@@ -3509,15 +3869,15 @@ var ReportReason = /* @__PURE__ */ ((ReportReason3) => {
|
|
|
3509
3869
|
var SupportedReportReasons = Object.values(ReportReason);
|
|
3510
3870
|
|
|
3511
3871
|
// src/report/report.schema.ts
|
|
3512
|
-
var ReportSchema =
|
|
3872
|
+
var ReportSchema = object35({
|
|
3513
3873
|
type: mixed6().oneOf(SupportedResourceTypes).required().label("Type"),
|
|
3514
|
-
resourceId:
|
|
3874
|
+
resourceId: string26().required().label("Resource ID"),
|
|
3515
3875
|
reason: mixed6().oneOf(SupportedReportReasons).required("Please choose a reason").label("Reason"),
|
|
3516
|
-
comment:
|
|
3876
|
+
comment: string26().max(1e3).optional().label("Comment")
|
|
3517
3877
|
}).label("Report Schema");
|
|
3518
3878
|
|
|
3519
3879
|
// src/feedback/feedback-filter.schema.ts
|
|
3520
|
-
import { object as
|
|
3880
|
+
import { object as object36 } from "yup";
|
|
3521
3881
|
|
|
3522
3882
|
// src/feedback/feedback.constant.ts
|
|
3523
3883
|
var FeedbackType = /* @__PURE__ */ ((FeedbackType3) => {
|
|
@@ -3540,7 +3900,7 @@ var SupportedFeedbackStatuses = Object.values(FeedbackStatus);
|
|
|
3540
3900
|
var FEEDBACK_MESSAGE_MAX = 4e3;
|
|
3541
3901
|
|
|
3542
3902
|
// src/feedback/feedback-filter.schema.ts
|
|
3543
|
-
var FeedbackFilterShape =
|
|
3903
|
+
var FeedbackFilterShape = object36({
|
|
3544
3904
|
type: enumFacet(SupportedFeedbackTypes, "Type"),
|
|
3545
3905
|
status: enumFacet(SupportedFeedbackStatuses, "Status")
|
|
3546
3906
|
}).label("Feedback Filter");
|
|
@@ -3550,18 +3910,18 @@ var FeedbackFilterSchema = rejectUnknownKeys(
|
|
|
3550
3910
|
);
|
|
3551
3911
|
|
|
3552
3912
|
// src/feedback/feedback.schema.ts
|
|
3553
|
-
import { mixed as mixed7, object as
|
|
3554
|
-
var FeedbackSchema =
|
|
3913
|
+
import { mixed as mixed7, object as object37, string as string27 } from "yup";
|
|
3914
|
+
var FeedbackSchema = object37({
|
|
3555
3915
|
type: mixed7().oneOf(SupportedFeedbackTypes).required().label("Type"),
|
|
3556
|
-
message:
|
|
3557
|
-
email:
|
|
3558
|
-
pageUrl:
|
|
3916
|
+
message: string27().trim().required("Please enter a message").max(FEEDBACK_MESSAGE_MAX).label("Message"),
|
|
3917
|
+
email: string27().email().optional().label("Email"),
|
|
3918
|
+
pageUrl: string27().optional().label("Page URL"),
|
|
3559
3919
|
status: mixed7().oneOf(SupportedFeedbackStatuses).optional().label("Status")
|
|
3560
3920
|
}).label("Feedback Schema");
|
|
3561
3921
|
|
|
3562
3922
|
// src/skill/skill-filter.schema.ts
|
|
3563
|
-
import { object as
|
|
3564
|
-
var SkillFilterShape =
|
|
3923
|
+
import { object as object38 } from "yup";
|
|
3924
|
+
var SkillFilterShape = object38({
|
|
3565
3925
|
/** Display names, matched exactly. Free-text matching is `?search=`. */
|
|
3566
3926
|
names: tokens("Names"),
|
|
3567
3927
|
/**
|
|
@@ -3579,7 +3939,7 @@ var SkillFilterSchema = rejectUnknownKeys(
|
|
|
3579
3939
|
);
|
|
3580
3940
|
|
|
3581
3941
|
// src/billing/billing.schema.ts
|
|
3582
|
-
import { array as
|
|
3942
|
+
import { array as array18, boolean as boolean19, number as number18, object as object39, string as string28 } from "yup";
|
|
3583
3943
|
|
|
3584
3944
|
// src/billing/billing.constant.ts
|
|
3585
3945
|
var PlanKind = /* @__PURE__ */ ((PlanKind2) => {
|
|
@@ -3682,60 +4042,60 @@ var GRANT_MAX_LIMIT = 1e5;
|
|
|
3682
4042
|
var CONSUME_MAX_UNITS = 1e3;
|
|
3683
4043
|
|
|
3684
4044
|
// src/billing/billing.schema.ts
|
|
3685
|
-
var MoneySchema =
|
|
3686
|
-
amountMinor:
|
|
4045
|
+
var MoneySchema = object39({
|
|
4046
|
+
amountMinor: number18().integer().min(0).required().label("Amount (minor units)"),
|
|
3687
4047
|
/**
|
|
3688
4048
|
* ISO-4217, as returned by Paddle. Deliberately NOT constrained to
|
|
3689
4049
|
* `SupportedSalaryCurrencies`: that list is the job-salary vocabulary, and
|
|
3690
4050
|
* Paddle bills in currencies it does not contain. Constraining it here would
|
|
3691
4051
|
* reject real payments.
|
|
3692
4052
|
*/
|
|
3693
|
-
currencyCode:
|
|
4053
|
+
currencyCode: string28().trim().uppercase().required().label("Currency")
|
|
3694
4054
|
}).label("Money");
|
|
3695
|
-
var PlanGrantSchema =
|
|
4055
|
+
var PlanGrantSchema = object39({
|
|
3696
4056
|
/**
|
|
3697
4057
|
* The gated capability, e.g. `job.post`. A free string on purpose: see the
|
|
3698
4058
|
* note on `FeatureKey` in `billing.constant.ts`. Adding a feature to the
|
|
3699
4059
|
* paywall must not require releasing this package.
|
|
3700
4060
|
*/
|
|
3701
|
-
feature:
|
|
3702
|
-
type:
|
|
4061
|
+
feature: string28().trim().required().min(1).max(64).label("Feature"),
|
|
4062
|
+
type: string28().oneOf(SupportedGrantTypes).required().default("boolean" /* Boolean */).label("Grant Type"),
|
|
3703
4063
|
/** Uses per period. Required for `quota`, meaningless otherwise. */
|
|
3704
|
-
limit:
|
|
4064
|
+
limit: number18().integer().min(1).max(GRANT_MAX_LIMIT).optional().label("Limit").when("type", {
|
|
3705
4065
|
is: "quota" /* Quota */,
|
|
3706
4066
|
then: (s) => s.required("A quota grant needs a limit")
|
|
3707
4067
|
}),
|
|
3708
|
-
period:
|
|
4068
|
+
period: string28().oneOf(SupportedQuotaPeriods).optional().default("month" /* Month */).label("Period").when("type", {
|
|
3709
4069
|
is: "quota" /* Quota */,
|
|
3710
4070
|
then: (s) => s.required("A quota grant needs a period")
|
|
3711
4071
|
}),
|
|
3712
4072
|
/** Non-expiring uses granted per purchase. Required for `credit`. */
|
|
3713
|
-
amount:
|
|
4073
|
+
amount: number18().integer().min(1).max(GRANT_MAX_LIMIT).optional().label("Amount").when("type", {
|
|
3714
4074
|
is: "credit" /* Credit */,
|
|
3715
4075
|
then: (s) => s.required("A credit grant needs an amount")
|
|
3716
4076
|
})
|
|
3717
4077
|
}).label("Plan Grant");
|
|
3718
|
-
var PaddlePriceIdsSchema =
|
|
3719
|
-
sandbox:
|
|
3720
|
-
live:
|
|
4078
|
+
var PaddlePriceIdsSchema = object39({
|
|
4079
|
+
sandbox: array18().of(string28().required()).optional().default([]).label("Sandbox Price IDs"),
|
|
4080
|
+
live: array18().of(string28().required()).optional().default([]).label("Live Price IDs")
|
|
3721
4081
|
}).label("Paddle Price IDs");
|
|
3722
|
-
var PlanSchema =
|
|
4082
|
+
var PlanSchema = object39({
|
|
3723
4083
|
/** Stable slug, e.g. `employer-pro`. The identifier everything else references. */
|
|
3724
|
-
key:
|
|
3725
|
-
name:
|
|
3726
|
-
description:
|
|
3727
|
-
kind:
|
|
4084
|
+
key: string28().trim().lowercase().required().min(2).max(64).label("Key"),
|
|
4085
|
+
name: string28().required().min(1).max(120).label("Name"),
|
|
4086
|
+
description: string28().optional().max(500).label("Description"),
|
|
4087
|
+
kind: string28().oneOf(SupportedPlanKinds).required().default("subscription" /* Subscription */).label("Kind"),
|
|
3728
4088
|
/**
|
|
3729
4089
|
* The line this plan belongs to; a user holds one live subscription per line.
|
|
3730
4090
|
* Optional on purpose: absent is the default line, so existing plans need no
|
|
3731
4091
|
* migration and together still read as "one plan at a time".
|
|
3732
4092
|
*/
|
|
3733
|
-
audience:
|
|
3734
|
-
status:
|
|
4093
|
+
audience: string28().oneOf(SupportedPlanAudiences).optional().label("Audience"),
|
|
4094
|
+
status: string28().oneOf(SupportedPlanStatuses).required().default("draft" /* Draft */).label("Status"),
|
|
3735
4095
|
paddlePriceIds: PaddlePriceIdsSchema.default(
|
|
3736
4096
|
() => PaddlePriceIdsSchema.getDefault()
|
|
3737
4097
|
),
|
|
3738
|
-
grants:
|
|
4098
|
+
grants: array18().of(PlanGrantSchema.required()).required().min(1, "A plan must grant at least one feature").label("Grants"),
|
|
3739
4099
|
/** Display price for the pricing page. Billing is always Paddle's own figure. */
|
|
3740
4100
|
price: MoneySchema.optional().default(void 0).label("Price"),
|
|
3741
4101
|
/**
|
|
@@ -3748,416 +4108,84 @@ var PlanSchema = object38({
|
|
|
3748
4108
|
* a subscription as "monthly" at the use site rather than defaulting it here,
|
|
3749
4109
|
* so an unset value stays visible as unset.
|
|
3750
4110
|
*/
|
|
3751
|
-
billingPeriod:
|
|
4111
|
+
billingPeriod: string28().oneOf(SupportedBillingPeriods).optional().label("Billing Period"),
|
|
3752
4112
|
/** Ordering on the pricing page, ascending. */
|
|
3753
|
-
sortOrder:
|
|
3754
|
-
createdAt:
|
|
3755
|
-
updatedAt:
|
|
4113
|
+
sortOrder: number18().integer().optional().default(0).label("Sort Order"),
|
|
4114
|
+
createdAt: number18().optional().label("Created At"),
|
|
4115
|
+
updatedAt: number18().optional().label("Updated At")
|
|
3756
4116
|
}).label("Plan");
|
|
3757
|
-
var BillingSubscriptionSchema =
|
|
3758
|
-
userId:
|
|
3759
|
-
paddleSubscriptionId:
|
|
3760
|
-
paddleCustomerId:
|
|
3761
|
-
planKey:
|
|
3762
|
-
priceIds:
|
|
3763
|
-
status:
|
|
4117
|
+
var BillingSubscriptionSchema = object39({
|
|
4118
|
+
userId: string28().required().label("User ID"),
|
|
4119
|
+
paddleSubscriptionId: string28().required().label("Paddle Subscription ID"),
|
|
4120
|
+
paddleCustomerId: string28().required().label("Paddle Customer ID"),
|
|
4121
|
+
planKey: string28().required().label("Plan Key"),
|
|
4122
|
+
priceIds: array18().of(string28().required()).required().label("Price IDs"),
|
|
4123
|
+
status: string28().oneOf(SupportedSubscriptionStatuses).required().label("Status"),
|
|
3764
4124
|
/** Start of the period the quota is anchored to. */
|
|
3765
|
-
currentPeriodStart:
|
|
3766
|
-
currentPeriodEnd:
|
|
4125
|
+
currentPeriodStart: number18().optional().label("Current Period Start"),
|
|
4126
|
+
currentPeriodEnd: number18().optional().label("Current Period End"),
|
|
3767
4127
|
/** Set when a cancellation is scheduled; access continues until the period ends. */
|
|
3768
|
-
canceledAt:
|
|
4128
|
+
canceledAt: number18().optional().label("Canceled At"),
|
|
3769
4129
|
/**
|
|
3770
4130
|
* A downgrade booked for the end of the current period: the plan key it moves
|
|
3771
4131
|
* to at renewal. Upgrades never set this; they apply immediately. Mirrored
|
|
3772
4132
|
* from the Paddle subscription's own `customData`, so Paddle stays the source
|
|
3773
4133
|
* of truth.
|
|
3774
4134
|
*/
|
|
3775
|
-
pendingPlanKey:
|
|
4135
|
+
pendingPlanKey: string28().optional().label("Pending Plan Key"),
|
|
3776
4136
|
/** `occurred_at` of the newest Paddle event applied. Guards out-of-order writes. */
|
|
3777
|
-
lastEventAt:
|
|
3778
|
-
createdAt:
|
|
3779
|
-
updatedAt:
|
|
4137
|
+
lastEventAt: number18().required().default(0).label("Last Event At"),
|
|
4138
|
+
createdAt: number18().optional().label("Created At"),
|
|
4139
|
+
updatedAt: number18().optional().label("Updated At")
|
|
3780
4140
|
}).label("Billing Subscription");
|
|
3781
|
-
var BillingTransactionSchema =
|
|
3782
|
-
userId:
|
|
3783
|
-
paddleTransactionId:
|
|
3784
|
-
paddleCustomerId:
|
|
3785
|
-
paddleSubscriptionId:
|
|
3786
|
-
planKey:
|
|
3787
|
-
priceIds:
|
|
3788
|
-
status:
|
|
4141
|
+
var BillingTransactionSchema = object39({
|
|
4142
|
+
userId: string28().required().label("User ID"),
|
|
4143
|
+
paddleTransactionId: string28().required().label("Paddle Transaction ID"),
|
|
4144
|
+
paddleCustomerId: string28().optional().label("Paddle Customer ID"),
|
|
4145
|
+
paddleSubscriptionId: string28().optional().label("Paddle Subscription ID"),
|
|
4146
|
+
planKey: string28().optional().label("Plan Key"),
|
|
4147
|
+
priceIds: array18().of(string28().required()).required().label("Price IDs"),
|
|
4148
|
+
status: string28().oneOf(SupportedTransactionStatuses).required().label("Status"),
|
|
3789
4149
|
total: MoneySchema.optional().default(void 0).label("Total"),
|
|
3790
|
-
billedAt:
|
|
3791
|
-
lastEventAt:
|
|
3792
|
-
createdAt:
|
|
3793
|
-
updatedAt:
|
|
4150
|
+
billedAt: number18().optional().label("Billed At"),
|
|
4151
|
+
lastEventAt: number18().required().default(0).label("Last Event At"),
|
|
4152
|
+
createdAt: number18().optional().label("Created At"),
|
|
4153
|
+
updatedAt: number18().optional().label("Updated At")
|
|
3794
4154
|
}).label("Billing Transaction");
|
|
3795
|
-
var FeatureEntitlementSchema =
|
|
3796
|
-
feature:
|
|
3797
|
-
type:
|
|
4155
|
+
var FeatureEntitlementSchema = object39({
|
|
4156
|
+
feature: string28().required().label("Feature"),
|
|
4157
|
+
type: string28().oneOf(SupportedGrantTypes).required().label("Grant Type"),
|
|
3798
4158
|
/** Whether the feature may be used AT ALL right now. */
|
|
3799
|
-
allowed:
|
|
4159
|
+
allowed: boolean19().required().default(false).label("Allowed"),
|
|
3800
4160
|
/** Quota ceiling for the period. Absent for boolean grants. */
|
|
3801
|
-
limit:
|
|
3802
|
-
used:
|
|
4161
|
+
limit: number18().integer().optional().label("Limit"),
|
|
4162
|
+
used: number18().integer().optional().label("Used"),
|
|
3803
4163
|
/** Quota remaining plus any credit balance. Absent for boolean grants. */
|
|
3804
|
-
remaining:
|
|
4164
|
+
remaining: number18().integer().optional().label("Remaining"),
|
|
3805
4165
|
/** Non-expiring balance from one-off purchases. */
|
|
3806
|
-
credits:
|
|
4166
|
+
credits: number18().integer().optional().label("Credits"),
|
|
3807
4167
|
/** When the quota next refreshes. */
|
|
3808
|
-
resetsAt:
|
|
4168
|
+
resetsAt: number18().optional().label("Resets At")
|
|
3809
4169
|
}).label("Feature Entitlement");
|
|
3810
|
-
var EntitlementDenialSchema =
|
|
3811
|
-
feature:
|
|
3812
|
-
reason:
|
|
3813
|
-
limit:
|
|
3814
|
-
used:
|
|
3815
|
-
resetsAt:
|
|
3816
|
-
upgradeUrl:
|
|
4170
|
+
var EntitlementDenialSchema = object39({
|
|
4171
|
+
feature: string28().required().label("Feature"),
|
|
4172
|
+
reason: string28().oneOf(SupportedEntitlementDenialReasons).required().label("Reason"),
|
|
4173
|
+
limit: number18().integer().optional().label("Limit"),
|
|
4174
|
+
used: number18().integer().optional().label("Used"),
|
|
4175
|
+
resetsAt: number18().optional().label("Resets At"),
|
|
4176
|
+
upgradeUrl: string28().optional().label("Upgrade URL")
|
|
3817
4177
|
}).label("Entitlement Denial");
|
|
3818
|
-
var EntitlementConsumeSchema =
|
|
3819
|
-
userId:
|
|
3820
|
-
feature:
|
|
3821
|
-
units:
|
|
4178
|
+
var EntitlementConsumeSchema = object39({
|
|
4179
|
+
userId: string28().required().label("User ID"),
|
|
4180
|
+
feature: string28().trim().required().min(1).max(64).label("Feature"),
|
|
4181
|
+
units: number18().integer().min(1).max(CONSUME_MAX_UNITS).required().default(1).label("Units"),
|
|
3822
4182
|
/**
|
|
3823
4183
|
* Caller-supplied de-duplication key. Makes a spend exactly-once across the
|
|
3824
4184
|
* retries that at-least-once delivery and ordinary network failures produce.
|
|
3825
4185
|
*/
|
|
3826
|
-
idempotencyKey:
|
|
4186
|
+
idempotencyKey: string28().trim().required().min(8).max(128).label("Idempotency Key")
|
|
3827
4187
|
}).label("Entitlement Consume");
|
|
3828
4188
|
|
|
3829
|
-
// src/taxonomy/taxonomy.schema.ts
|
|
3830
|
-
import { array as array18, boolean as boolean19, number as number18, object as object39, string as string28 } from "yup";
|
|
3831
|
-
|
|
3832
|
-
// src/taxonomy/taxonomy.constant.ts
|
|
3833
|
-
var SupportedConceptSources = ["seed", "manual", "import"];
|
|
3834
|
-
var SupportedTaxonomySources = ["seed", "manual"];
|
|
3835
|
-
var SupportedTaxonomyNodeKinds = ["category", "subcategory", "group"];
|
|
3836
|
-
var SupportedPrefilterRules = [
|
|
3837
|
-
"spoken_language",
|
|
3838
|
-
"no_letters",
|
|
3839
|
-
"url_or_email",
|
|
3840
|
-
"fragment"
|
|
3841
|
-
];
|
|
3842
|
-
var SupportedUnmatchedResolutions = ["pending", "added", "rejected"];
|
|
3843
|
-
var SupportedUnmatchedReasons = ["ambiguous"];
|
|
3844
|
-
var SupportedProposalKinds = ["concept", "alias", "placement"];
|
|
3845
|
-
var SupportedProposalFilters = [...SupportedProposalKinds, "uncategorized"];
|
|
3846
|
-
var SupportedProposalStatuses = ["draft", "approved", "discarded"];
|
|
3847
|
-
var SupportedProposalMessageRoles = ["operator", "model"];
|
|
3848
|
-
var SupportedResolverVerdicts = [
|
|
3849
|
-
"same",
|
|
3850
|
-
"narrower",
|
|
3851
|
-
"broader",
|
|
3852
|
-
"related",
|
|
3853
|
-
"new_concept",
|
|
3854
|
-
"out_of_scope"
|
|
3855
|
-
];
|
|
3856
|
-
var SupportedAuditStatuses = ["pending", "correct", "wrong"];
|
|
3857
|
-
var SupportedReleaseKinds = ["seed", "manual", "review", "auto"];
|
|
3858
|
-
var SupportedReleaseStatuses = ["open", "rolled_back"];
|
|
3859
|
-
var SupportedMatchTypes = ["exact", "canonical", "fuzzy", "none"];
|
|
3860
|
-
var SupportedTaxonomyJobKinds = [
|
|
3861
|
-
"describe-concepts",
|
|
3862
|
-
"retype-concepts",
|
|
3863
|
-
"generate-aliases",
|
|
3864
|
-
"alias-backfill",
|
|
3865
|
-
"close-gaps",
|
|
3866
|
-
"propose-placements",
|
|
3867
|
-
"place-unplaced",
|
|
3868
|
-
"calibrate-placement",
|
|
3869
|
-
"reseed",
|
|
3870
|
-
"reindex",
|
|
3871
|
-
"calibrate-resolver",
|
|
3872
|
-
"golden",
|
|
3873
|
-
"resolve-backlog"
|
|
3874
|
-
];
|
|
3875
|
-
var SupportedTaxonomyJobStates = ["running", "succeeded", "failed", "cancelled"];
|
|
3876
|
-
var CONCEPT_ID_PATTERN = /^[a-z][a-z0-9-]*_[a-z0-9-]+$/;
|
|
3877
|
-
var VOCABULARY_ID_PATTERN = /^[a-z][a-z0-9-]{0,31}$/;
|
|
3878
|
-
var TAXONOMY_NODE_ID_PATTERN = /^[A-Za-z0-9_]{1,64}$/;
|
|
3879
|
-
var CONCEPT_TYPE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
|
|
3880
|
-
var SCHEDULE_MIN_HOURS = 1;
|
|
3881
|
-
var SCHEDULE_MAX_HOURS = 24 * 30;
|
|
3882
|
-
|
|
3883
|
-
// src/taxonomy/taxonomy.schema.ts
|
|
3884
|
-
var conceptId = () => string28().matches(CONCEPT_ID_PATTERN, "conceptId must look like <vocabulary>_<slug>");
|
|
3885
|
-
var vocabularyId = () => string28().matches(VOCABULARY_ID_PATTERN, "vocabulary must match [a-z][a-z0-9-]{0,31}");
|
|
3886
|
-
var nodeId = () => string28().matches(TAXONOMY_NODE_ID_PATTERN, "node ids look like CAT_01_TECH");
|
|
3887
|
-
var strings = () => array18(string28().trim().min(1).required());
|
|
3888
|
-
var VocabularyTypeSchema = object39({
|
|
3889
|
-
id: string28().matches(CONCEPT_TYPE_PATTERN, "type ids match [a-z][a-z0-9_]*").required().label("Type ID"),
|
|
3890
|
-
/** Shown to the model when it classifies a concept; include examples. */
|
|
3891
|
-
description: string28().defined().label("Description")
|
|
3892
|
-
}).label("Vocabulary Type");
|
|
3893
|
-
var VocabularySchema = object39({
|
|
3894
|
-
vocabularyId: vocabularyId().required().label("Vocabulary ID"),
|
|
3895
|
-
name: string28().trim().min(1).required().label("Name"),
|
|
3896
|
-
/** What one concept is called in prompts and messages: "skill", "role". */
|
|
3897
|
-
noun: string28().trim().min(1).required().label("Noun"),
|
|
3898
|
-
nounPlural: string28().trim().min(1).required().label("Noun (plural)"),
|
|
3899
|
-
/** What belongs in this vocabulary, for prompts. */
|
|
3900
|
-
description: string28().trim().min(1).required().label("Description"),
|
|
3901
|
-
/** Where its terms are written, for the "swap it in" test: "a job posting". */
|
|
3902
|
-
appearsIn: string28().trim().min(1).required().label("Appears In"),
|
|
3903
|
-
/** Things that are NOT concepts of this vocabulary, for prompts. */
|
|
3904
|
-
outOfScope: string28().trim().min(1).required().label("Out of Scope"),
|
|
3905
|
-
types: array18(VocabularyTypeSchema.required()).min(1).required().test(
|
|
3906
|
-
"unique-type-ids",
|
|
3907
|
-
"type ids must be unique",
|
|
3908
|
-
(types) => !types || new Set(types.map((t) => t.id)).size === types.length
|
|
3909
|
-
).label("Types"),
|
|
3910
|
-
/** The type a concept gets when nothing more specific is known. One of `types`. */
|
|
3911
|
-
defaultType: string28().required().test("default-type-listed", "defaultType must be one of the type ids", function(value) {
|
|
3912
|
-
const types = this.parent.types ?? [];
|
|
3913
|
-
return types.some((t) => t.id === value);
|
|
3914
|
-
}).label("Default Type"),
|
|
3915
|
-
prefilter: array18(string28().oneOf(SupportedPrefilterRules).required()).default([]).label("Prefilter"),
|
|
3916
|
-
/**
|
|
3917
|
-
* Words that qualify a term without changing what it names ("fluent",
|
|
3918
|
-
* "native"), dropped from its canonical lookup form in THIS vocabulary only.
|
|
3919
|
-
*/
|
|
3920
|
-
qualifiers: strings().optional().label("Qualifiers"),
|
|
3921
|
-
/** Extra instructions per model task; each is optional. */
|
|
3922
|
-
guidance: object39({
|
|
3923
|
-
describe: string28().optional(),
|
|
3924
|
-
classify: string28().optional(),
|
|
3925
|
-
aliases: string28().optional(),
|
|
3926
|
-
verify: string28().optional(),
|
|
3927
|
-
place: string28().optional()
|
|
3928
|
-
}).label("Guidance"),
|
|
3929
|
-
source: string28().oneOf(SupportedTaxonomySources).required().label("Source"),
|
|
3930
|
-
createdAt: number18().required().label("Created At"),
|
|
3931
|
-
updatedAt: number18().required().label("Updated At")
|
|
3932
|
-
}).label("Vocabulary");
|
|
3933
|
-
var TaxonomyNodeSchema = object39({
|
|
3934
|
-
nodeId: nodeId().required().label("Node ID"),
|
|
3935
|
-
vocabulary: vocabularyId().required().label("Vocabulary"),
|
|
3936
|
-
kind: string28().oneOf(SupportedTaxonomyNodeKinds).required().label("Kind"),
|
|
3937
|
-
name: string28().trim().min(1).required().label("Name"),
|
|
3938
|
-
/** Groups carry a description; categories and subcategories usually do not. */
|
|
3939
|
-
description: string28().optional().label("Description"),
|
|
3940
|
-
aliases: strings().default([]).label("Aliases"),
|
|
3941
|
-
/** null for categories (the roots). */
|
|
3942
|
-
parentId: string28().nullable().defined().label("Parent ID"),
|
|
3943
|
-
/** Ancestor ids from the root down to and including this node. */
|
|
3944
|
-
path: array18(string28().required()).default([]).label("Path"),
|
|
3945
|
-
source: string28().oneOf(SupportedTaxonomySources).optional().label("Source"),
|
|
3946
|
-
createdAt: number18().required().label("Created At"),
|
|
3947
|
-
updatedAt: number18().required().label("Updated At")
|
|
3948
|
-
}).label("Taxonomy Node");
|
|
3949
|
-
var ConceptMembershipSchema = object39({
|
|
3950
|
-
categoryId: string28().required().label("Category ID"),
|
|
3951
|
-
categoryName: string28().required().label("Category Name"),
|
|
3952
|
-
subcategoryId: string28().required().label("Subcategory ID"),
|
|
3953
|
-
subcategoryName: string28().required().label("Subcategory Name"),
|
|
3954
|
-
groupId: string28().required().label("Group ID"),
|
|
3955
|
-
groupName: string28().required().label("Group Name"),
|
|
3956
|
-
/** The source record this membership came from; '' when written by hand. */
|
|
3957
|
-
sourceId: string28().default("").label("Source ID")
|
|
3958
|
-
}).label("Concept Membership");
|
|
3959
|
-
var ConceptSchema = object39({
|
|
3960
|
-
/** `<vocabulary>_<slug>`, e.g. `skills_power-bi`. Primary key. */
|
|
3961
|
-
conceptId: conceptId().required().label("Concept ID"),
|
|
3962
|
-
vocabulary: vocabularyId().required().label("Vocabulary"),
|
|
3963
|
-
name: string28().trim().min(1).max(200).required().label("Name"),
|
|
3964
|
-
/** One of the vocabulary's type ids; checked against the vocabulary by the service. */
|
|
3965
|
-
type: string28().trim().matches(CONCEPT_TYPE_PATTERN, "type must be a type id").required().label("Type"),
|
|
3966
|
-
description: string28().trim().max(2e3).default("").label("Description"),
|
|
3967
|
-
aliases: strings().default([]).label("Aliases"),
|
|
3968
|
-
memberships: array18(ConceptMembershipSchema.required()).default([]).label("Memberships"),
|
|
3969
|
-
/** Always concepts of the same vocabulary. */
|
|
3970
|
-
relatedConceptIds: strings().default([]).label("Related Concepts"),
|
|
3971
|
-
source: string28().oneOf(SupportedConceptSources).required().label("Source"),
|
|
3972
|
-
createdAt: number18().required().label("Created At"),
|
|
3973
|
-
updatedAt: number18().required().label("Updated At"),
|
|
3974
|
-
/** Resume marker of `retype-concepts`: set once the type was examined. */
|
|
3975
|
-
retypedAt: number18().optional().label("Retyped At"),
|
|
3976
|
-
/** Resume marker of `generate-aliases`: set even when no alias was added. */
|
|
3977
|
-
aliasedAt: number18().optional().label("Aliased At"),
|
|
3978
|
-
/** Set while `description` is a stand-in; removed once a real one is written. */
|
|
3979
|
-
descriptionPending: boolean19().isTrue().optional().label("Description Pending")
|
|
3980
|
-
}).label("Concept");
|
|
3981
|
-
var UnmatchedTermSchema = object39({
|
|
3982
|
-
vocabulary: vocabularyId().required().label("Vocabulary"),
|
|
3983
|
-
/** The raw input as first received. */
|
|
3984
|
-
term: string28().required().label("Term"),
|
|
3985
|
-
/** The dedupe key, so "React JS" and "react.js" share a row. */
|
|
3986
|
-
normalizedTerm: string28().required().label("Normalized Term"),
|
|
3987
|
-
count: number18().integer().min(0).required().label("Count"),
|
|
3988
|
-
firstSeenAt: number18().required().label("First Seen At"),
|
|
3989
|
-
lastSeenAt: number18().required().label("Last Seen At"),
|
|
3990
|
-
/** Which callers sent it, capped. */
|
|
3991
|
-
sources: array18(
|
|
3992
|
-
object39({
|
|
3993
|
-
service: string28().required(),
|
|
3994
|
-
context: string28().optional()
|
|
3995
|
-
}).required()
|
|
3996
|
-
).default([]).label("Sources"),
|
|
3997
|
-
resolution: string28().oneOf(SupportedUnmatchedResolutions).required().label("Resolution"),
|
|
3998
|
-
resolvedConceptId: string28().optional().label("Resolved Concept"),
|
|
3999
|
-
/** `ambiguous`: the concept exists; the caller gave no context that picked one. */
|
|
4000
|
-
reason: string28().oneOf(SupportedUnmatchedReasons).optional().label("Reason"),
|
|
4001
|
-
/** The concepts an ambiguous term could mean. */
|
|
4002
|
-
candidates: array18(string28().required()).optional().label("Candidates"),
|
|
4003
|
-
/** Who rejected it when it was not a human: `prefilter:<rule>` or `model`. */
|
|
4004
|
-
rejectedBy: string28().optional().label("Rejected By")
|
|
4005
|
-
}).label("Unmatched Term");
|
|
4006
|
-
var ProposalCandidateSchema = object39({
|
|
4007
|
-
groupId: string28().required(),
|
|
4008
|
-
groupName: string28().required(),
|
|
4009
|
-
subcategoryId: string28().required(),
|
|
4010
|
-
subcategoryName: string28().required(),
|
|
4011
|
-
categoryId: string28().required(),
|
|
4012
|
-
categoryName: string28().required(),
|
|
4013
|
-
score: number18().required(),
|
|
4014
|
-
nearestMembers: array18(string28().required()).default([])
|
|
4015
|
-
}).label("Proposal Candidate");
|
|
4016
|
-
var ProposalMessageSchema = object39({
|
|
4017
|
-
role: string28().oneOf(SupportedProposalMessageRoles).required(),
|
|
4018
|
-
/** The operator's instruction, or the model's note on what it changed and why. */
|
|
4019
|
-
content: string28().defined(),
|
|
4020
|
-
/** Which model answered. Absent on operator turns. */
|
|
4021
|
-
model: string28().optional(),
|
|
4022
|
-
at: number18().required()
|
|
4023
|
-
}).label("Proposal Message");
|
|
4024
|
-
var SuggestedHomeSchema = object39({
|
|
4025
|
-
categoryName: string28().defined(),
|
|
4026
|
-
subcategoryName: string28().defined(),
|
|
4027
|
-
groupName: string28().defined()
|
|
4028
|
-
}).label("Suggested Home");
|
|
4029
|
-
var ResolverVerificationSchema = object39({
|
|
4030
|
-
verdict: string28().oneOf(SupportedResolverVerdicts).required(),
|
|
4031
|
-
/** The shown concept the verdict is about; null for `new_concept` / `out_of_scope`. */
|
|
4032
|
-
conceptId: string28().nullable().defined(),
|
|
4033
|
-
evidence: string28().defined(),
|
|
4034
|
-
/** What the judge took the term to mean, abbreviations expanded. */
|
|
4035
|
-
means: string28().optional(),
|
|
4036
|
-
/** For `same`: did the independent confirm step agree? */
|
|
4037
|
-
consistent: boolean19().optional(),
|
|
4038
|
-
confirm: object39({
|
|
4039
|
-
answer: string28().oneOf(["yes", "no"]).required(),
|
|
4040
|
-
evidence: string28().defined()
|
|
4041
|
-
}).optional().default(void 0)
|
|
4042
|
-
}).label("Resolver Verification");
|
|
4043
|
-
var ConceptProposalSchema = object39({
|
|
4044
|
-
proposalId: string28().required().label("Proposal ID"),
|
|
4045
|
-
vocabulary: vocabularyId().required().label("Vocabulary"),
|
|
4046
|
-
/** The miss this closes; `normalizedTerm` links back to its unmatched row. */
|
|
4047
|
-
term: string28().required().label("Term"),
|
|
4048
|
-
normalizedTerm: string28().required().label("Normalized Term"),
|
|
4049
|
-
/** Demand at proposal time, so the queue ranks without a join. */
|
|
4050
|
-
count: number18().integer().min(0).required().label("Count"),
|
|
4051
|
-
/** Other misses folded into this draft because they name the same new concept. */
|
|
4052
|
-
mergedTerms: array18(
|
|
4053
|
-
object39({
|
|
4054
|
-
term: string28().required(),
|
|
4055
|
-
normalizedTerm: string28().required(),
|
|
4056
|
-
count: number18().integer().min(0).required()
|
|
4057
|
-
}).required()
|
|
4058
|
-
).optional().label("Merged Terms"),
|
|
4059
|
-
/** Absent on drafts that predate the field: read as `concept`. */
|
|
4060
|
-
kind: string28().oneOf(SupportedProposalKinds).optional().label("Kind"),
|
|
4061
|
-
/** For `alias`: the concept the term names. For `placement`: the concept to place. */
|
|
4062
|
-
targetConceptId: string28().optional().label("Target Concept"),
|
|
4063
|
-
targetName: string28().optional().label("Target Name"),
|
|
4064
|
-
// The draft concept, editable until approved. Empty strings are valid here:
|
|
4065
|
-
// an alias draft carries no draft concept of its own.
|
|
4066
|
-
name: string28().defined().label("Name"),
|
|
4067
|
-
type: string28().defined().label("Type"),
|
|
4068
|
-
description: string28().defined().label("Description"),
|
|
4069
|
-
aliases: array18(string28().required()).default([]).label("Aliases"),
|
|
4070
|
-
/** Empty when no group fits: approved that way, the concept goes live uncategorized. */
|
|
4071
|
-
memberships: array18(ConceptMembershipSchema.required()).default([]).label("Memberships"),
|
|
4072
|
-
suggestedHome: SuggestedHomeSchema.nullable().optional().default(void 0).label("Suggested Home"),
|
|
4073
|
-
/** Resolved to concepts that exist; names that matched nothing are dropped. */
|
|
4074
|
-
relatedConceptIds: array18(string28().required()).default([]).label("Related Concepts"),
|
|
4075
|
-
/** Every placement candidate considered, best first. */
|
|
4076
|
-
candidates: array18(ProposalCandidateSchema.required()).default([]).label("Candidates"),
|
|
4077
|
-
needsReview: boolean19().required().label("Needs Review"),
|
|
4078
|
-
reason: string28().optional().label("Reason"),
|
|
4079
|
-
verification: ResolverVerificationSchema.optional().default(void 0).label("Verification"),
|
|
4080
|
-
/** What the auto-accept gate decided for an alias verdict. */
|
|
4081
|
-
autoAccept: object39({
|
|
4082
|
-
accepted: boolean19().required(),
|
|
4083
|
-
reason: string28().optional(),
|
|
4084
|
-
tier: string28().optional(),
|
|
4085
|
-
calibrationId: string28().optional(),
|
|
4086
|
-
releaseId: string28().optional()
|
|
4087
|
-
}).optional().default(void 0).label("Auto Accept"),
|
|
4088
|
-
/** Which model drafted it. */
|
|
4089
|
-
model: string28().defined().label("Model"),
|
|
4090
|
-
/** The refinement conversation, oldest first. */
|
|
4091
|
-
messages: array18(ProposalMessageSchema.required()).default([]).label("Messages"),
|
|
4092
|
-
status: string28().oneOf(SupportedProposalStatuses).required().label("Status"),
|
|
4093
|
-
/** Set when approved: the concept this became. */
|
|
4094
|
-
resolvedConceptId: string28().optional().label("Resolved Concept"),
|
|
4095
|
-
createdAt: number18().required().label("Created At"),
|
|
4096
|
-
updatedAt: number18().required().label("Updated At")
|
|
4097
|
-
}).label("Concept Proposal");
|
|
4098
|
-
var ReleaseSchema = object39({
|
|
4099
|
-
releaseId: string28().required().label("Release ID"),
|
|
4100
|
-
kind: string28().oneOf(SupportedReleaseKinds).required().label("Kind"),
|
|
4101
|
-
status: string28().oneOf(SupportedReleaseStatuses).required().label("Status"),
|
|
4102
|
-
createdBy: string28().required().label("Created By"),
|
|
4103
|
-
createdAt: number18().required().label("Created At"),
|
|
4104
|
-
note: string28().optional().label("Note"),
|
|
4105
|
-
rolledBackAt: number18().optional().label("Rolled Back At"),
|
|
4106
|
-
rolledBackBy: string28().optional().label("Rolled Back By")
|
|
4107
|
-
}).label("Release");
|
|
4108
|
-
var AuditSchema = object39({
|
|
4109
|
-
auditId: string28().required().label("Audit ID"),
|
|
4110
|
-
releaseId: string28().required().label("Release ID"),
|
|
4111
|
-
key: string28().required().label("Key"),
|
|
4112
|
-
term: string28().required().label("Term"),
|
|
4113
|
-
conceptId: string28().required().label("Concept ID"),
|
|
4114
|
-
tier: string28().optional().label("Tier"),
|
|
4115
|
-
status: string28().oneOf(SupportedAuditStatuses).required().label("Status"),
|
|
4116
|
-
createdAt: number18().required().label("Created At"),
|
|
4117
|
-
resolvedAt: number18().optional().label("Resolved At"),
|
|
4118
|
-
resolvedBy: string28().optional().label("Resolved By")
|
|
4119
|
-
}).label("Audit");
|
|
4120
|
-
var ResolverSettingsSchema = object39({
|
|
4121
|
-
autoAccept: object39({
|
|
4122
|
-
/** Stop switch for WRITING auto keys. Off until a calibration passes. */
|
|
4123
|
-
enabled: boolean19().required(),
|
|
4124
|
-
/** Minimum calibrated precision (Wilson 95% lower bound) for a tier to auto-accept. */
|
|
4125
|
-
target: number18().min(0.5).lessThan(1).required(),
|
|
4126
|
-
maxPerRun: number18().integer().min(0).required(),
|
|
4127
|
-
maxPerDay: number18().integer().min(0).required(),
|
|
4128
|
-
/** Share of each auto release sampled into the audit queue (0..1). */
|
|
4129
|
-
auditRate: number18().min(0).max(1).required()
|
|
4130
|
-
}).label("Auto Accept"),
|
|
4131
|
-
/** Stop switch for SERVING auto keys: false hides them from lookups at once. */
|
|
4132
|
-
serveAutoKeys: boolean19().required().label("Serve Auto Keys"),
|
|
4133
|
-
/** Minimum Meilisearch ranking score (0..1) for a fuzzy hit to be considered. */
|
|
4134
|
-
fuzzyMinScore: number18().min(0).max(1).required().label("Fuzzy Min Score"),
|
|
4135
|
-
/** Per-client limit on public search, per minute per replica. 0 disables it. */
|
|
4136
|
-
searchRateLimitPerMin: number18().integer().min(0).required().label("Search Rate Limit"),
|
|
4137
|
-
/** Master switch for scheduled maintenance jobs. */
|
|
4138
|
-
scheduledJobs: boolean19().required().label("Scheduled Jobs"),
|
|
4139
|
-
updatedAt: number18().optional().label("Updated At"),
|
|
4140
|
-
updatedBy: string28().optional().label("Updated By"),
|
|
4141
|
-
/** Why auto-accept was last switched off automatically. */
|
|
4142
|
-
trippedReason: string28().optional().label("Tripped Reason")
|
|
4143
|
-
}).label("Resolver Settings");
|
|
4144
|
-
var TaxonomyScheduleSchema = object39({
|
|
4145
|
-
kind: string28().oneOf(SupportedTaxonomyJobKinds).required().label("Job"),
|
|
4146
|
-
enabled: boolean19().required().label("Enabled"),
|
|
4147
|
-
everyHours: number18().min(SCHEDULE_MIN_HOURS).max(SCHEDULE_MAX_HOURS).required().label("Every (hours)"),
|
|
4148
|
-
/** Last time the scheduler fired this job. */
|
|
4149
|
-
lastRunAt: number18().optional().label("Last Run At"),
|
|
4150
|
-
updatedAt: number18().required().label("Updated At")
|
|
4151
|
-
}).label("Taxonomy Schedule");
|
|
4152
|
-
var NormalizeContextSchema = object39({
|
|
4153
|
-
/** The title of the record the terms come from. */
|
|
4154
|
-
title: string28().max(200).optional(),
|
|
4155
|
-
/** Backbone node ids (category, subcategory or group). */
|
|
4156
|
-
categoryIds: array18(nodeId().required()).max(10).optional(),
|
|
4157
|
-
/** Other terms from the same record, in the same vocabulary. */
|
|
4158
|
-
siblings: array18(string28().max(200).required()).max(50).optional()
|
|
4159
|
-
}).label("Normalize Context");
|
|
4160
|
-
|
|
4161
4189
|
// src/taxonomy/taxonomy-filter.schema.ts
|
|
4162
4190
|
import { boolean as boolean20, object as object40 } from "yup";
|
|
4163
4191
|
var ConceptFilterShape = object40({
|