lancer-shared 1.2.192 → 1.2.194
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/bundle.cjs.js +354 -163
- package/dist/schemas/bidder/bid.d.ts +19 -0
- package/dist/schemas/campaign/campaign-analytics.d.ts +32 -0
- package/dist/schemas/campaign/campaign-search.d.ts +88 -0
- package/dist/schemas/campaign/campaign.d.ts +24 -0
- package/dist/schemas/campaign/index.d.ts +1 -0
- package/dist/schemas/campaign/sequence/client-size-node.d.ts +10 -10
- package/dist/schemas/campaign/sequence/client-spent-node.d.ts +18 -18
- package/dist/schemas/campaign/sequence/edges.d.ts +0 -8
- package/dist/schemas/campaign/sequence/hire-rate-node.d.ts +18 -18
- package/dist/schemas/campaign/sequence/hourly-rate-node.d.ts +18 -18
- package/dist/schemas/campaign/sequence/index.d.ts +0 -3
- package/dist/schemas/campaign/sequence/node-types.d.ts +1 -1
- package/dist/schemas/campaign/sequence/rating-node.d.ts +18 -18
- package/dist/schemas/campaign/sequence/suitability-node.d.ts +18 -18
- package/dist/schemas/job/job-filters.d.ts +3 -0
- package/dist/schemas/scraper/scrape-payload.d.ts +12 -0
- package/package.json +1 -1
package/dist/bundle.cjs.js
CHANGED
|
@@ -11136,6 +11136,7 @@ const jobFiltersSchema = z.object({
|
|
|
11136
11136
|
excludes: z.string().nullable(),
|
|
11137
11137
|
})
|
|
11138
11138
|
.nullable(),
|
|
11139
|
+
searchQuery: z.string().nullable(),
|
|
11139
11140
|
isFeatured: z.enum(['all', 'true', 'false']).nullable(),
|
|
11140
11141
|
regions: z.array(regionEnum).nullable(),
|
|
11141
11142
|
categories: z
|
|
@@ -13107,6 +13108,7 @@ const campaignSchema = z.object({
|
|
|
13107
13108
|
// automatedSuitability: z.boolean().nullable(),
|
|
13108
13109
|
boostingEnabled: z.boolean().nullable().default(false),
|
|
13109
13110
|
maximumBoost: z.number().nullable().default(30),
|
|
13111
|
+
minBoost: z.number().nullable().default(1),
|
|
13110
13112
|
boostDownToNthPlace: z.number().min(1).max(4).nullable(),
|
|
13111
13113
|
connectsAbovePrevious: z.number().min(1).nullable(),
|
|
13112
13114
|
insufficeintBoostConnectsAction: insufficeintBoostConnectsActionEnum,
|
|
@@ -13406,6 +13408,354 @@ const campaignInsightsSchema = z.object({
|
|
|
13406
13408
|
suitabilityRange90to100: z.number(),
|
|
13407
13409
|
});
|
|
13408
13410
|
|
|
13411
|
+
// Clean, validated schema using Zod
|
|
13412
|
+
const SearchFieldsSchema = z.object({
|
|
13413
|
+
allWords: z.string().default(''),
|
|
13414
|
+
anyWords: z.string().default(''),
|
|
13415
|
+
noneWords: z.string().default(''),
|
|
13416
|
+
exactPhrase: z.string().default(''),
|
|
13417
|
+
titleSearch: z.string().default(''),
|
|
13418
|
+
skillsSearch: z.string().default(''),
|
|
13419
|
+
});
|
|
13420
|
+
class SearchQueryBuilder {
|
|
13421
|
+
/**
|
|
13422
|
+
* Build a search query from structured fields
|
|
13423
|
+
* Properly handles quoted phrases in all fields
|
|
13424
|
+
*/
|
|
13425
|
+
static buildQuery(fields) {
|
|
13426
|
+
const validatedFields = SearchFieldsSchema.parse(fields);
|
|
13427
|
+
const parts = [];
|
|
13428
|
+
// All words (AND logic) - supports phrases
|
|
13429
|
+
if (validatedFields.allWords.trim()) {
|
|
13430
|
+
const terms = this.splitTerms(validatedFields.allWords);
|
|
13431
|
+
if (terms.length === 1) {
|
|
13432
|
+
parts.push(terms[0]);
|
|
13433
|
+
}
|
|
13434
|
+
else if (terms.length > 1) {
|
|
13435
|
+
parts.push(`(${terms.join(' AND ')})`);
|
|
13436
|
+
}
|
|
13437
|
+
}
|
|
13438
|
+
// Any words (OR logic) - supports phrases
|
|
13439
|
+
if (validatedFields.anyWords.trim()) {
|
|
13440
|
+
const terms = this.splitTerms(validatedFields.anyWords);
|
|
13441
|
+
if (terms.length === 1) {
|
|
13442
|
+
parts.push(terms[0]);
|
|
13443
|
+
}
|
|
13444
|
+
else if (terms.length > 1) {
|
|
13445
|
+
parts.push(`(${terms.join(' OR ')})`);
|
|
13446
|
+
}
|
|
13447
|
+
}
|
|
13448
|
+
// Excluded words (NOT logic) - supports phrases
|
|
13449
|
+
if (validatedFields.noneWords.trim()) {
|
|
13450
|
+
const terms = this.splitTerms(validatedFields.noneWords);
|
|
13451
|
+
const notParts = terms.map((term) => `NOT ${term}`);
|
|
13452
|
+
parts.push(...notParts);
|
|
13453
|
+
}
|
|
13454
|
+
// Exact phrase - single phrase only
|
|
13455
|
+
if (validatedFields.exactPhrase.trim()) {
|
|
13456
|
+
parts.push(`"${validatedFields.exactPhrase.trim()}"`);
|
|
13457
|
+
}
|
|
13458
|
+
// Field-specific searches - now support phrases too
|
|
13459
|
+
if (validatedFields.titleSearch.trim()) {
|
|
13460
|
+
const titleTerms = this.splitTerms(validatedFields.titleSearch);
|
|
13461
|
+
if (titleTerms.length === 1) {
|
|
13462
|
+
// Single term or phrase
|
|
13463
|
+
const term = titleTerms[0];
|
|
13464
|
+
const isQuoted = term.startsWith('"') && term.endsWith('"');
|
|
13465
|
+
parts.push(isQuoted ? `title:${term}` : `title:"${term}"`);
|
|
13466
|
+
}
|
|
13467
|
+
else {
|
|
13468
|
+
// Multiple terms - each gets title: prefix
|
|
13469
|
+
const titleParts = titleTerms.map((term) => {
|
|
13470
|
+
const isQuoted = term.startsWith('"') && term.endsWith('"');
|
|
13471
|
+
return isQuoted ? `title:${term}` : `title:"${term}"`;
|
|
13472
|
+
});
|
|
13473
|
+
parts.push(`(${titleParts.join(' AND ')})`);
|
|
13474
|
+
}
|
|
13475
|
+
}
|
|
13476
|
+
if (validatedFields.skillsSearch.trim()) {
|
|
13477
|
+
const skillsTerms = this.splitTerms(validatedFields.skillsSearch);
|
|
13478
|
+
if (skillsTerms.length === 1) {
|
|
13479
|
+
const term = skillsTerms[0];
|
|
13480
|
+
const isQuoted = term.startsWith('"') && term.endsWith('"');
|
|
13481
|
+
parts.push(isQuoted ? `skills:${term}` : `skills:"${term}"`);
|
|
13482
|
+
}
|
|
13483
|
+
else {
|
|
13484
|
+
const skillsParts = skillsTerms.map((term) => {
|
|
13485
|
+
const isQuoted = term.startsWith('"') && term.endsWith('"');
|
|
13486
|
+
return isQuoted ? `skills:${term}` : `skills:"${term}"`;
|
|
13487
|
+
});
|
|
13488
|
+
parts.push(`(${skillsParts.join(' AND ')})`);
|
|
13489
|
+
}
|
|
13490
|
+
}
|
|
13491
|
+
return parts.join(' AND ').trim() || '*';
|
|
13492
|
+
}
|
|
13493
|
+
/**
|
|
13494
|
+
* Get human-readable description of search criteria
|
|
13495
|
+
*/
|
|
13496
|
+
static describe(fields) {
|
|
13497
|
+
const validatedFields = SearchFieldsSchema.parse(fields);
|
|
13498
|
+
const descriptions = [];
|
|
13499
|
+
if (validatedFields.allWords)
|
|
13500
|
+
descriptions.push(`All words: ${validatedFields.allWords}`);
|
|
13501
|
+
if (validatedFields.anyWords)
|
|
13502
|
+
descriptions.push(`Any words: ${validatedFields.anyWords}`);
|
|
13503
|
+
if (validatedFields.noneWords)
|
|
13504
|
+
descriptions.push(`Excluding: ${validatedFields.noneWords}`);
|
|
13505
|
+
if (validatedFields.exactPhrase)
|
|
13506
|
+
descriptions.push(`Exact phrase: "${validatedFields.exactPhrase}"`);
|
|
13507
|
+
if (validatedFields.titleSearch)
|
|
13508
|
+
descriptions.push(`In title: ${validatedFields.titleSearch}`);
|
|
13509
|
+
if (validatedFields.skillsSearch)
|
|
13510
|
+
descriptions.push(`Skills: ${validatedFields.skillsSearch}`);
|
|
13511
|
+
return descriptions.length > 0 ? descriptions.join(' • ') : 'All results';
|
|
13512
|
+
}
|
|
13513
|
+
/**
|
|
13514
|
+
* Validate search fields
|
|
13515
|
+
*/
|
|
13516
|
+
static validate(fields) {
|
|
13517
|
+
const result = SearchFieldsSchema.safeParse(fields);
|
|
13518
|
+
if (result.success) {
|
|
13519
|
+
return { valid: true, data: result.data };
|
|
13520
|
+
}
|
|
13521
|
+
return {
|
|
13522
|
+
valid: false,
|
|
13523
|
+
errors: result.error.errors.map((err) => `${err.path.join('.')}: ${err.message}`),
|
|
13524
|
+
};
|
|
13525
|
+
}
|
|
13526
|
+
/**
|
|
13527
|
+
* Test/demo method to show phrase handling
|
|
13528
|
+
* Remove this in production
|
|
13529
|
+
*/
|
|
13530
|
+
static examples() {
|
|
13531
|
+
const testFields = {
|
|
13532
|
+
allWords: 'react "senior developer" javascript',
|
|
13533
|
+
anyWords: 'frontend "full stack" backend',
|
|
13534
|
+
noneWords: 'junior "entry level"',
|
|
13535
|
+
exactPhrase: 'tech lead',
|
|
13536
|
+
titleSearch: 'software engineer',
|
|
13537
|
+
skillsSearch: 'node.js',
|
|
13538
|
+
};
|
|
13539
|
+
console.log('Query:', this.buildQuery(testFields));
|
|
13540
|
+
console.log('Description:', this.describe(testFields));
|
|
13541
|
+
// Should produce:
|
|
13542
|
+
// Query: (react AND "senior developer" AND javascript) AND (frontend OR "full stack" OR backend) AND NOT junior AND NOT "entry level" AND "tech lead" AND title:"software engineer" AND skills:"node.js"
|
|
13543
|
+
}
|
|
13544
|
+
/**
|
|
13545
|
+
* Parse a query string back into SearchFields structure
|
|
13546
|
+
* Fixed to handle grouped NOT expressions correctly
|
|
13547
|
+
*/
|
|
13548
|
+
static parseQuery(query) {
|
|
13549
|
+
const result = {
|
|
13550
|
+
allWords: '',
|
|
13551
|
+
anyWords: '',
|
|
13552
|
+
noneWords: '',
|
|
13553
|
+
exactPhrase: '',
|
|
13554
|
+
titleSearch: '',
|
|
13555
|
+
skillsSearch: '',
|
|
13556
|
+
};
|
|
13557
|
+
if (!query || query.trim() === '*') {
|
|
13558
|
+
return result;
|
|
13559
|
+
}
|
|
13560
|
+
let remainingQuery = query.trim();
|
|
13561
|
+
// 1. Extract field-specific searches first
|
|
13562
|
+
const fieldPatterns = [
|
|
13563
|
+
{ field: 'titleSearch', pattern: /title:("([^"]+)"|([^\s)]+))/g },
|
|
13564
|
+
{ field: 'skillsSearch', pattern: /skills:("([^"]+)"|([^\s)]+))/g },
|
|
13565
|
+
];
|
|
13566
|
+
for (const { field, pattern } of fieldPatterns) {
|
|
13567
|
+
const matches = [...remainingQuery.matchAll(pattern)];
|
|
13568
|
+
if (matches.length > 0) {
|
|
13569
|
+
const terms = matches.map((match) => {
|
|
13570
|
+
if (match[2]) {
|
|
13571
|
+
return `"${match[2]}"`;
|
|
13572
|
+
}
|
|
13573
|
+
return match[3];
|
|
13574
|
+
});
|
|
13575
|
+
result[field] = terms.join(' ');
|
|
13576
|
+
remainingQuery = remainingQuery.replace(pattern, '').trim();
|
|
13577
|
+
}
|
|
13578
|
+
}
|
|
13579
|
+
// 2. Extract grouped field searches like title:(term1 AND term2)
|
|
13580
|
+
const groupedFieldPattern = /(title|skills):\(([^)]+)\)/g;
|
|
13581
|
+
let groupMatch;
|
|
13582
|
+
while ((groupMatch = groupedFieldPattern.exec(remainingQuery)) !== null) {
|
|
13583
|
+
const fieldName = groupMatch[1] === 'title' ? 'titleSearch' : 'skillsSearch';
|
|
13584
|
+
const groupContent = groupMatch[2];
|
|
13585
|
+
const terms = this.extractTermsFromGroup(groupContent, groupMatch[1]);
|
|
13586
|
+
result[fieldName] = terms.join(' ');
|
|
13587
|
+
remainingQuery = remainingQuery.replace(groupMatch[0], '').trim();
|
|
13588
|
+
}
|
|
13589
|
+
// 3. **NEW** - Extract grouped NOT expressions FIRST: NOT (term1 OR term2 OR ...)
|
|
13590
|
+
const groupedNotPattern = /NOT\s+\(([^)]+(?:\s+OR\s+[^)]+)+)\)/g;
|
|
13591
|
+
const groupedNotMatches = [...remainingQuery.matchAll(groupedNotPattern)];
|
|
13592
|
+
if (groupedNotMatches.length > 0) {
|
|
13593
|
+
const noneTerms = this.extractTermsFromORGroup(groupedNotMatches[0][1]);
|
|
13594
|
+
result.noneWords = noneTerms.join(' ');
|
|
13595
|
+
remainingQuery = remainingQuery
|
|
13596
|
+
.replace(groupedNotMatches[0][0], '')
|
|
13597
|
+
.trim();
|
|
13598
|
+
}
|
|
13599
|
+
// 4. Extract individual NOT terms (only if no grouped NOT was found)
|
|
13600
|
+
if (!result.noneWords) {
|
|
13601
|
+
const notPattern = /NOT\s+("([^"]+)"|([^\s)]+))/g;
|
|
13602
|
+
const notMatches = [...remainingQuery.matchAll(notPattern)];
|
|
13603
|
+
if (notMatches.length > 0) {
|
|
13604
|
+
const noneTerms = notMatches.map((match) => {
|
|
13605
|
+
if (match[2]) {
|
|
13606
|
+
return `"${match[2]}"`;
|
|
13607
|
+
}
|
|
13608
|
+
return match[3];
|
|
13609
|
+
});
|
|
13610
|
+
result.noneWords = noneTerms.join(' ');
|
|
13611
|
+
remainingQuery = remainingQuery.replace(notPattern, '').trim();
|
|
13612
|
+
}
|
|
13613
|
+
}
|
|
13614
|
+
// 5. Process grouped expressions - Clean up connectors first
|
|
13615
|
+
remainingQuery = this.cleanupConnectors(remainingQuery);
|
|
13616
|
+
// Extract OR groups (anyWords) - PROCESS FIRST
|
|
13617
|
+
const orGroupPattern = /\(([^)]+(?:\s+OR\s+[^)]+)+)\)/g;
|
|
13618
|
+
const orMatches = [...remainingQuery.matchAll(orGroupPattern)];
|
|
13619
|
+
if (orMatches.length > 0) {
|
|
13620
|
+
const orTerms = this.extractTermsFromORGroup(orMatches[0][1]);
|
|
13621
|
+
result.anyWords = orTerms.join(' ');
|
|
13622
|
+
remainingQuery = remainingQuery.replace(orMatches[0][0], '').trim();
|
|
13623
|
+
}
|
|
13624
|
+
// Extract AND groups (allWords) - PROCESS SECOND
|
|
13625
|
+
const andGroupPattern = /\(([^)]+(?:\s+AND\s+[^)]+)+)\)/g;
|
|
13626
|
+
const andMatches = [...remainingQuery.matchAll(andGroupPattern)];
|
|
13627
|
+
if (andMatches.length > 0) {
|
|
13628
|
+
const andTerms = this.extractTermsFromANDGroup(andMatches[0][1]);
|
|
13629
|
+
result.allWords = andTerms.join(' ');
|
|
13630
|
+
remainingQuery = remainingQuery.replace(andMatches[0][0], '').trim();
|
|
13631
|
+
}
|
|
13632
|
+
// 6. Extract standalone quoted phrases (exactPhrase) - ONLY after all groups processed
|
|
13633
|
+
const standaloneQuotePattern = /(?:^|\s)("([^"]+)")(?=\s|$)/g;
|
|
13634
|
+
const quoteMatches = [...remainingQuery.matchAll(standaloneQuotePattern)];
|
|
13635
|
+
if (quoteMatches.length > 0) {
|
|
13636
|
+
result.exactPhrase = quoteMatches[0][2];
|
|
13637
|
+
remainingQuery = remainingQuery.replace(quoteMatches[0][1], '').trim();
|
|
13638
|
+
}
|
|
13639
|
+
// 7. Handle remaining simple terms as allWords
|
|
13640
|
+
if (remainingQuery) {
|
|
13641
|
+
const remainingTerms = this.splitTermsWithQuotes(remainingQuery);
|
|
13642
|
+
if (remainingTerms.length > 0) {
|
|
13643
|
+
result.allWords = result.allWords
|
|
13644
|
+
? `${result.allWords} ${remainingTerms.join(' ')}`.trim()
|
|
13645
|
+
: remainingTerms.join(' ');
|
|
13646
|
+
}
|
|
13647
|
+
}
|
|
13648
|
+
return result;
|
|
13649
|
+
}
|
|
13650
|
+
/**
|
|
13651
|
+
* Extract terms from grouped field content, preserving quotes
|
|
13652
|
+
*/
|
|
13653
|
+
static extractTermsFromGroup(content, fieldPrefix) {
|
|
13654
|
+
// Remove field prefixes but preserve quotes
|
|
13655
|
+
let cleanContent = content;
|
|
13656
|
+
const fieldPattern = new RegExp(`${fieldPrefix}:("([^"]+)"|([^\\s]+))`, 'g');
|
|
13657
|
+
const terms = [];
|
|
13658
|
+
let match;
|
|
13659
|
+
while ((match = fieldPattern.exec(content)) !== null) {
|
|
13660
|
+
if (match[2]) {
|
|
13661
|
+
// Quoted content
|
|
13662
|
+
terms.push(`"${match[2]}"`);
|
|
13663
|
+
}
|
|
13664
|
+
else if (match[3]) {
|
|
13665
|
+
// Unquoted content
|
|
13666
|
+
terms.push(match[3]);
|
|
13667
|
+
}
|
|
13668
|
+
}
|
|
13669
|
+
return terms.length > 0
|
|
13670
|
+
? terms
|
|
13671
|
+
: [
|
|
13672
|
+
cleanContent
|
|
13673
|
+
.replace(/(title|skills):/g, '')
|
|
13674
|
+
.replace(/\s+AND\s+/g, ' ')
|
|
13675
|
+
.trim(),
|
|
13676
|
+
];
|
|
13677
|
+
}
|
|
13678
|
+
/**
|
|
13679
|
+
* Extract terms from OR group, preserving quotes and removing OR separators
|
|
13680
|
+
*/
|
|
13681
|
+
static extractTermsFromORGroup(content) {
|
|
13682
|
+
return content
|
|
13683
|
+
.split(/\s+OR\s+/)
|
|
13684
|
+
.map((term) => term.trim())
|
|
13685
|
+
.filter(Boolean);
|
|
13686
|
+
}
|
|
13687
|
+
/**
|
|
13688
|
+
* Extract terms from AND group, preserving quotes
|
|
13689
|
+
*/
|
|
13690
|
+
static extractTermsFromANDGroup(content) {
|
|
13691
|
+
return content
|
|
13692
|
+
.split(/\s+AND\s+/)
|
|
13693
|
+
.map((term) => term.trim())
|
|
13694
|
+
.filter(Boolean);
|
|
13695
|
+
}
|
|
13696
|
+
/**
|
|
13697
|
+
* Split simple terms while preserving quoted phrases
|
|
13698
|
+
*/
|
|
13699
|
+
static splitTermsWithQuotes(input) {
|
|
13700
|
+
const terms = [];
|
|
13701
|
+
const regex = /"([^"]+)"|(\S+)/g;
|
|
13702
|
+
let match;
|
|
13703
|
+
while ((match = regex.exec(input)) !== null) {
|
|
13704
|
+
if (match[1]) {
|
|
13705
|
+
// Quoted phrase - preserve quotes
|
|
13706
|
+
terms.push(`"${match[1]}"`);
|
|
13707
|
+
}
|
|
13708
|
+
else if (match[2] && !['AND', 'OR', 'NOT'].includes(match[2])) {
|
|
13709
|
+
// Regular word (not a connector)
|
|
13710
|
+
terms.push(match[2]);
|
|
13711
|
+
}
|
|
13712
|
+
}
|
|
13713
|
+
return terms;
|
|
13714
|
+
}
|
|
13715
|
+
/**
|
|
13716
|
+
* Clean up AND/OR connectors while preserving quotes
|
|
13717
|
+
*/
|
|
13718
|
+
static cleanupConnectors(query) {
|
|
13719
|
+
return query
|
|
13720
|
+
.replace(/\s+AND\s+/g, ' ')
|
|
13721
|
+
.replace(/\s+/g, ' ')
|
|
13722
|
+
.trim();
|
|
13723
|
+
}
|
|
13724
|
+
/**
|
|
13725
|
+
* Test the roundtrip conversion (buildQuery -> parseQuery)
|
|
13726
|
+
*/
|
|
13727
|
+
static testRoundtrip(fields) {
|
|
13728
|
+
const query = this.buildQuery(fields);
|
|
13729
|
+
const parsed = this.parseQuery(query);
|
|
13730
|
+
const matches = JSON.stringify(fields) === JSON.stringify(parsed);
|
|
13731
|
+
return {
|
|
13732
|
+
original: fields,
|
|
13733
|
+
query,
|
|
13734
|
+
parsed,
|
|
13735
|
+
matches,
|
|
13736
|
+
};
|
|
13737
|
+
}
|
|
13738
|
+
/**
|
|
13739
|
+
* Simple term splitting that handles quoted phrases
|
|
13740
|
+
*/
|
|
13741
|
+
static splitTerms(input) {
|
|
13742
|
+
const terms = [];
|
|
13743
|
+
const regex = /"([^"]+)"|(\S+)/g;
|
|
13744
|
+
let match;
|
|
13745
|
+
while ((match = regex.exec(input)) !== null) {
|
|
13746
|
+
if (match[1]) {
|
|
13747
|
+
// Quoted phrase
|
|
13748
|
+
terms.push(`"${match[1]}"`);
|
|
13749
|
+
}
|
|
13750
|
+
else if (match[2]) {
|
|
13751
|
+
// Regular word
|
|
13752
|
+
terms.push(match[2]);
|
|
13753
|
+
}
|
|
13754
|
+
}
|
|
13755
|
+
return terms;
|
|
13756
|
+
}
|
|
13757
|
+
}
|
|
13758
|
+
|
|
13409
13759
|
const fallbackEnum = z.enum(['noBoostBid', 'ignore']);
|
|
13410
13760
|
const boostFormSchema = z.object({
|
|
13411
13761
|
minPlace: z.number().int().min(1).max(4), // Don't boost below this place
|
|
@@ -13416,144 +13766,14 @@ const boostFormSchema = z.object({
|
|
|
13416
13766
|
const nodeEnums = z.enum([
|
|
13417
13767
|
'clientAvgHourlyRateNode',
|
|
13418
13768
|
'bidNode',
|
|
13769
|
+
'suitabilityNode',
|
|
13419
13770
|
'clientHireRateNode',
|
|
13420
13771
|
'clientSizeNode',
|
|
13421
13772
|
'boostNode',
|
|
13422
13773
|
'clientSpentNode',
|
|
13423
13774
|
'clientRatingNode',
|
|
13424
|
-
'startNode',
|
|
13425
|
-
'budgetNode',
|
|
13426
|
-
'paymentTypeNode',
|
|
13427
13775
|
]);
|
|
13428
13776
|
|
|
13429
|
-
const rangesOverlap$5 = (range1, range2) => {
|
|
13430
|
-
return range1.min < range2.max && range2.min < range1.max;
|
|
13431
|
-
};
|
|
13432
|
-
// Payment type options (excluding 'Unspecified')
|
|
13433
|
-
const budgetPaymentTypeEnum = z.enum(['Hourly', 'Fixed-price']);
|
|
13434
|
-
const createBudgetFormSchema = (existingHourlyRateRanges, existingFixedBudgetRanges) => z
|
|
13435
|
-
.object({
|
|
13436
|
-
paymentTypes: z
|
|
13437
|
-
.array(budgetPaymentTypeEnum)
|
|
13438
|
-
.min(1, 'Please select at least one payment type'),
|
|
13439
|
-
hourlyRateMin: z
|
|
13440
|
-
.number()
|
|
13441
|
-
.min(0, 'Minimum hourly rate must be non-negative')
|
|
13442
|
-
.optional(),
|
|
13443
|
-
hourlyRateMax: z
|
|
13444
|
-
.number()
|
|
13445
|
-
.min(0, 'Maximum hourly rate must be non-negative')
|
|
13446
|
-
.optional(),
|
|
13447
|
-
fixedBudgetMin: z
|
|
13448
|
-
.number()
|
|
13449
|
-
.min(0, 'Minimum fixed budget must be non-negative')
|
|
13450
|
-
.optional(),
|
|
13451
|
-
fixedBudgetMax: z
|
|
13452
|
-
.number()
|
|
13453
|
-
.min(0, 'Maximum fixed budget must be non-negative')
|
|
13454
|
-
.optional(),
|
|
13455
|
-
action: nodeEnums,
|
|
13456
|
-
})
|
|
13457
|
-
// Validate hourly rate fields are provided when Hourly is selected
|
|
13458
|
-
.refine((data) => {
|
|
13459
|
-
if (data.paymentTypes.includes('Hourly')) {
|
|
13460
|
-
return (data.hourlyRateMin !== undefined && data.hourlyRateMax !== undefined);
|
|
13461
|
-
}
|
|
13462
|
-
return true;
|
|
13463
|
-
}, {
|
|
13464
|
-
message: 'Hourly rate min and max are required when Hourly payment type is selected',
|
|
13465
|
-
path: ['hourlyRateMin'],
|
|
13466
|
-
})
|
|
13467
|
-
// Validate fixed budget fields are provided when Fixed-price is selected
|
|
13468
|
-
.refine((data) => {
|
|
13469
|
-
if (data.paymentTypes.includes('Fixed-price')) {
|
|
13470
|
-
return (data.fixedBudgetMin !== undefined &&
|
|
13471
|
-
data.fixedBudgetMax !== undefined);
|
|
13472
|
-
}
|
|
13473
|
-
return true;
|
|
13474
|
-
}, {
|
|
13475
|
-
message: 'Fixed budget min and max are required when Fixed-price payment type is selected',
|
|
13476
|
-
path: ['fixedBudgetMin'],
|
|
13477
|
-
})
|
|
13478
|
-
// Validate hourly rate range when provided AND hourly payment type is selected
|
|
13479
|
-
.refine((data) => {
|
|
13480
|
-
if (data.paymentTypes.includes('Hourly') &&
|
|
13481
|
-
data.hourlyRateMin !== undefined &&
|
|
13482
|
-
data.hourlyRateMax !== undefined) {
|
|
13483
|
-
return data.hourlyRateMin < data.hourlyRateMax;
|
|
13484
|
-
}
|
|
13485
|
-
return true;
|
|
13486
|
-
}, {
|
|
13487
|
-
message: 'Minimum hourly rate must be less than maximum',
|
|
13488
|
-
path: ['hourlyRateMin'],
|
|
13489
|
-
})
|
|
13490
|
-
.refine((data) => {
|
|
13491
|
-
if (data.paymentTypes.includes('Hourly') &&
|
|
13492
|
-
data.hourlyRateMin !== undefined &&
|
|
13493
|
-
data.hourlyRateMax !== undefined) {
|
|
13494
|
-
return data.hourlyRateMax > data.hourlyRateMin;
|
|
13495
|
-
}
|
|
13496
|
-
return true;
|
|
13497
|
-
}, {
|
|
13498
|
-
message: 'Maximum hourly rate must be greater than minimum',
|
|
13499
|
-
path: ['hourlyRateMax'],
|
|
13500
|
-
})
|
|
13501
|
-
// Validate fixed budget range when provided AND fixed payment type is selected
|
|
13502
|
-
.refine((data) => {
|
|
13503
|
-
if (data.paymentTypes.includes('Fixed-price') &&
|
|
13504
|
-
data.fixedBudgetMin !== undefined &&
|
|
13505
|
-
data.fixedBudgetMax !== undefined) {
|
|
13506
|
-
return data.fixedBudgetMin < data.fixedBudgetMax;
|
|
13507
|
-
}
|
|
13508
|
-
return true;
|
|
13509
|
-
}, {
|
|
13510
|
-
message: 'Minimum fixed budget must be less than maximum',
|
|
13511
|
-
path: ['fixedBudgetMin'],
|
|
13512
|
-
})
|
|
13513
|
-
.refine((data) => {
|
|
13514
|
-
if (data.paymentTypes.includes('Fixed-price') &&
|
|
13515
|
-
data.fixedBudgetMin !== undefined &&
|
|
13516
|
-
data.fixedBudgetMax !== undefined) {
|
|
13517
|
-
return data.fixedBudgetMax > data.fixedBudgetMin;
|
|
13518
|
-
}
|
|
13519
|
-
return true;
|
|
13520
|
-
}, {
|
|
13521
|
-
message: 'Maximum fixed budget must be greater than minimum',
|
|
13522
|
-
path: ['fixedBudgetMax'],
|
|
13523
|
-
})
|
|
13524
|
-
// Check for hourly rate range overlaps when provided
|
|
13525
|
-
.refine((data) => {
|
|
13526
|
-
if (data.hourlyRateMin !== undefined &&
|
|
13527
|
-
data.hourlyRateMax !== undefined) {
|
|
13528
|
-
const newHourlyRateRange = {
|
|
13529
|
-
min: data.hourlyRateMin,
|
|
13530
|
-
max: data.hourlyRateMax,
|
|
13531
|
-
};
|
|
13532
|
-
return !existingHourlyRateRanges.some((existingRange) => rangesOverlap$5(newHourlyRateRange, existingRange));
|
|
13533
|
-
}
|
|
13534
|
-
return true;
|
|
13535
|
-
}, {
|
|
13536
|
-
message: 'Hourly rate range overlaps with existing hourly rate range',
|
|
13537
|
-
path: ['hourlyRateMin'],
|
|
13538
|
-
})
|
|
13539
|
-
// Check for fixed budget range overlaps when provided
|
|
13540
|
-
.refine((data) => {
|
|
13541
|
-
if (data.fixedBudgetMin !== undefined &&
|
|
13542
|
-
data.fixedBudgetMax !== undefined) {
|
|
13543
|
-
const newFixedBudgetRange = {
|
|
13544
|
-
min: data.fixedBudgetMin,
|
|
13545
|
-
max: data.fixedBudgetMax,
|
|
13546
|
-
};
|
|
13547
|
-
return !existingFixedBudgetRanges.some((existingRange) => rangesOverlap$5(newFixedBudgetRange, existingRange));
|
|
13548
|
-
}
|
|
13549
|
-
return true;
|
|
13550
|
-
}, {
|
|
13551
|
-
message: 'Fixed budget range overlaps with existing fixed budget range',
|
|
13552
|
-
path: ['fixedBudgetMin'],
|
|
13553
|
-
});
|
|
13554
|
-
// Default schema for backwards compatibility
|
|
13555
|
-
const addBudgetNodeFormSchema = createBudgetFormSchema([], []);
|
|
13556
|
-
|
|
13557
13777
|
const sizeOverlap = (size1, size2) => {
|
|
13558
13778
|
if (size1 === size2) {
|
|
13559
13779
|
return true;
|
|
@@ -13654,27 +13874,6 @@ const createHourlyRateFormSchema = (existingRanges) => z
|
|
|
13654
13874
|
// Keep the original schema for backwards compatibility if needed
|
|
13655
13875
|
const addFromHourlyRateNodeFormSchema = createHourlyRateFormSchema([]);
|
|
13656
13876
|
|
|
13657
|
-
const limitedPaymentTypeEnum = paymentTypeEnum.exclude(['Unspecified']);
|
|
13658
|
-
const paymentTypeOverlap = (paymentType1, paymentType2) => {
|
|
13659
|
-
if (paymentType1 === paymentType2) {
|
|
13660
|
-
return true;
|
|
13661
|
-
}
|
|
13662
|
-
return false;
|
|
13663
|
-
};
|
|
13664
|
-
const createPaymentTypeFormSchema = (existingPaymentTypes) => z
|
|
13665
|
-
.object({
|
|
13666
|
-
paymentTypes: z.array(limitedPaymentTypeEnum).min(1),
|
|
13667
|
-
action: nodeEnums,
|
|
13668
|
-
})
|
|
13669
|
-
.refine((data) => {
|
|
13670
|
-
const newPaymentTypes = data.paymentTypes;
|
|
13671
|
-
return !existingPaymentTypes.some((existingPaymentType) => newPaymentTypes.some((newPaymentType) => paymentTypeOverlap(newPaymentType, existingPaymentType)));
|
|
13672
|
-
}, {
|
|
13673
|
-
message: 'Payment type overlaps with existing payment type',
|
|
13674
|
-
path: ['paymentTypes'],
|
|
13675
|
-
});
|
|
13676
|
-
const addFromPaymentTypeNodeFormSchema = createPaymentTypeFormSchema([]);
|
|
13677
|
-
|
|
13678
13877
|
const rangesOverlap$1 = (range1, range2) => {
|
|
13679
13878
|
return range1.from < range2.to && range2.from < range1.to;
|
|
13680
13879
|
};
|
|
@@ -13701,10 +13900,6 @@ const createClientRatingFormSchema = (existingRanges) => z
|
|
|
13701
13900
|
});
|
|
13702
13901
|
const addFromClientRatingNodeFormSchema = createClientRatingFormSchema([]);
|
|
13703
13902
|
|
|
13704
|
-
const addFromStartNodeFormSchema = z.object({
|
|
13705
|
-
action: nodeEnums,
|
|
13706
|
-
});
|
|
13707
|
-
|
|
13708
13903
|
const rangesOverlap = (range1, range2) => {
|
|
13709
13904
|
return range1.from < range2.to && range2.from < range1.to;
|
|
13710
13905
|
};
|
|
@@ -13740,6 +13935,7 @@ const bidPayloadProposalDataSchema = z.object({
|
|
|
13740
13935
|
boostingEnabled: z.boolean(),
|
|
13741
13936
|
specialisedProfileOptions: z.array(z.string()),
|
|
13742
13937
|
maximumBoost: z.number().nullable(),
|
|
13938
|
+
minimumBoost: z.number().nullable(),
|
|
13743
13939
|
boostDownToNthPlace: z.number().min(1).max(4).nullable(),
|
|
13744
13940
|
connectsAbovePrevious: z.number().min(1).nullable(),
|
|
13745
13941
|
insufficeintBoostConnectsAction: insufficeintBoostConnectsActionEnum,
|
|
@@ -23526,6 +23722,8 @@ exports.PuppeteerConnectionErrorException = PuppeteerConnectionErrorException;
|
|
|
23526
23722
|
exports.QuestionPairNotMatchingException = QuestionPairNotMatchingException;
|
|
23527
23723
|
exports.ROUTES = ROUTES;
|
|
23528
23724
|
exports.ScraperAccountProxyNotFoundException = ScraperAccountProxyNotFoundException;
|
|
23725
|
+
exports.SearchFieldsSchema = SearchFieldsSchema;
|
|
23726
|
+
exports.SearchQueryBuilder = SearchQueryBuilder;
|
|
23529
23727
|
exports.SelectAgencyException = SelectAgencyException;
|
|
23530
23728
|
exports.SelectContractorException = SelectContractorException;
|
|
23531
23729
|
exports.SelectorNotFoundError = SelectorNotFoundError;
|
|
@@ -23537,14 +23735,11 @@ exports.acceptUpworkInvitationSchema = acceptUpworkInvitationSchema;
|
|
|
23537
23735
|
exports.accountStatusDisplayMap = accountStatusDisplayMap;
|
|
23538
23736
|
exports.accountStatusOrder = accountStatusOrder;
|
|
23539
23737
|
exports.accountStatusSchema = accountStatusSchema;
|
|
23540
|
-
exports.addBudgetNodeFormSchema = addBudgetNodeFormSchema;
|
|
23541
23738
|
exports.addFromClientHireRateNodeFormSchema = addFromClientHireRateNodeFormSchema;
|
|
23542
23739
|
exports.addFromClientRatingNodeFormSchema = addFromClientRatingNodeFormSchema;
|
|
23543
23740
|
exports.addFromClientSizeNodeFormSchema = addFromClientSizeNodeFormSchema;
|
|
23544
23741
|
exports.addFromClientSpentNodeFormSchema = addFromClientSpentNodeFormSchema;
|
|
23545
23742
|
exports.addFromHourlyRateNodeFormSchema = addFromHourlyRateNodeFormSchema;
|
|
23546
|
-
exports.addFromPaymentTypeNodeFormSchema = addFromPaymentTypeNodeFormSchema;
|
|
23547
|
-
exports.addFromStartNodeFormSchema = addFromStartNodeFormSchema;
|
|
23548
23743
|
exports.addFromSuitabilityNodeFormSchema = addFromSuitabilityNodeFormSchema;
|
|
23549
23744
|
exports.agencyBidPayloadSchema = agencyBidPayloadSchema;
|
|
23550
23745
|
exports.agencyBidProposalDataSchema = agencyBidProposalDataSchema;
|
|
@@ -23579,7 +23774,6 @@ exports.biddingRejectedWithFeedbackEventMetadata = biddingRejectedWithFeedbackEv
|
|
|
23579
23774
|
exports.booleanSchema = booleanSchema;
|
|
23580
23775
|
exports.boostAboveMaxConnectsException = boostAboveMaxConnectsException;
|
|
23581
23776
|
exports.boostFormSchema = boostFormSchema;
|
|
23582
|
-
exports.budgetPaymentTypeEnum = budgetPaymentTypeEnum;
|
|
23583
23777
|
exports.buildRoute = buildRoute;
|
|
23584
23778
|
exports.campaignAIMetricsSchema = campaignAIMetricsSchema;
|
|
23585
23779
|
exports.campaignActivityCreateSchema = campaignActivityCreateSchema;
|
|
@@ -23612,7 +23806,6 @@ exports.convertToUtc = convertToUtc;
|
|
|
23612
23806
|
exports.countryMapping = countryMapping;
|
|
23613
23807
|
exports.coverLetterTemplateSchema = coverLetterTemplateSchema;
|
|
23614
23808
|
exports.createBidderAccountSchema = createBidderAccountSchema;
|
|
23615
|
-
exports.createBudgetFormSchema = createBudgetFormSchema;
|
|
23616
23809
|
exports.createCampaignSchema = createCampaignSchema;
|
|
23617
23810
|
exports.createChatbotSchema = createChatbotSchema;
|
|
23618
23811
|
exports.createClientHireRateFormSchema = createClientHireRateFormSchema;
|
|
@@ -23623,7 +23816,6 @@ exports.createCoverLetterTemplateSchema = createCoverLetterTemplateSchema;
|
|
|
23623
23816
|
exports.createHourlyRateFormSchema = createHourlyRateFormSchema;
|
|
23624
23817
|
exports.createOrganizationProfileSchema = createOrganizationProfileSchema;
|
|
23625
23818
|
exports.createOrganizationSchema = createOrganizationSchema;
|
|
23626
|
-
exports.createPaymentTypeFormSchema = createPaymentTypeFormSchema;
|
|
23627
23819
|
exports.createScraperAccountSchema = createScraperAccountSchema;
|
|
23628
23820
|
exports.createSuitabilityFormSchema = createSuitabilityFormSchema;
|
|
23629
23821
|
exports.dailyUsageSchema = dailyUsageSchema;
|
|
@@ -23715,7 +23907,6 @@ exports.leadSchema = leadSchema;
|
|
|
23715
23907
|
exports.leadStatusActivitySchema = leadStatusActivitySchema;
|
|
23716
23908
|
exports.leadStatusEnum = leadStatusEnum;
|
|
23717
23909
|
exports.leadStatusEventMetadata = leadStatusEventMetadata;
|
|
23718
|
-
exports.limitedPaymentTypeEnum = limitedPaymentTypeEnum;
|
|
23719
23910
|
exports.limitsSchema = limitsSchema;
|
|
23720
23911
|
exports.logEventSchema = logEventSchema;
|
|
23721
23912
|
exports.loginFailedException = loginFailedException;
|