openings 0.1.8 → 0.1.10

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openings",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Find evidence-grounded jobs, including relevant roles you may not have searched for, without accounts or API keys.",
5
5
  "author": { "name": "Openings contributors" },
6
6
  "license": "MIT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openings",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "A free, candidate-safe job-search substrate for AI agents",
5
5
  "license": "MIT",
6
6
  "repository": { "type": "git", "url": "git+https://github.com/abhay-avagama/hiring-agent.git" },
package/src/locations.ts CHANGED
@@ -101,12 +101,29 @@ function detectRegions(location: string, description: string): string[] {
101
101
  return [...new Set([...fromLocation, ...fromDescription])];
102
102
  }
103
103
 
104
+ /** US state and district postal codes. Many collide with ISO country codes (IN, CA, DE, CO, GA, ID, IL, LA, MA, MD, MO, MT, NE, PA, SC, SD, TN, VA). */
105
+ const US_STATE_CODES = new Set(["AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL", "GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY", "DC"]);
106
+ const US_CITY_STATE = /,\s*([A-Z]{2})(?=\s*(?:$|,|\d{5}|\(|\/|-))/g;
107
+ /** Workday's "ST-CITY, street" shape, e.g. "IN-INDIANAPOLIS, 220 VIRGINIA AVE". */
108
+ const US_STATE_PREFIX = /^([A-Z]{2})-(?=[A-Za-z])/;
109
+
104
110
  function detectCountries(location: string): string[] {
105
- const matches: string[] = [];
111
+ const byName: string[] = [];
112
+ const byCode: string[] = [];
106
113
  for (const rule of countryRules()) {
107
- if (rule.location.test(location) || rule.codeLocation.test(location)) matches.push(rule.code);
114
+ if (rule.location.test(location)) byName.push(rule.code);
115
+ else if (rule.codeLocation.test(location)) byCode.push(rule.code);
108
116
  }
109
- return matches;
117
+ // "Indianapolis, IN" is Indiana, not India: a bare code in the US "City, ST" position is a state unless a country name
118
+ // or a known Indian place says otherwise.
119
+ // Case matters: "Berlin, de" is a lowercase country code, "Gary, IN" is a state.
120
+ const prefix = US_STATE_PREFIX.exec(location)?.[1];
121
+ const stateCodes = [...[...location.matchAll(US_CITY_STATE)].map((match) => match[1]!), ...(prefix ? [prefix] : [])].filter((code) => US_STATE_CODES.has(code));
122
+ if (stateCodes.length && byName.length === 0 && !INDIA_PATTERN.test(location)) {
123
+ return [...new Set(["US", ...byCode.filter((code) => !US_STATE_CODES.has(code))])];
124
+ }
125
+ if (byName.includes("US")) return [...new Set([...byName, ...byCode.filter((code) => !US_STATE_CODES.has(code))])];
126
+ return [...new Set([...byName, ...byCode])];
110
127
  }
111
128
 
112
129
  function detectEligibleCountries(description: string): string[] {
package/src/providers.ts CHANGED
@@ -65,7 +65,7 @@ const smartrecruiters: ProviderSpec = {
65
65
  normalize(company, job) {
66
66
  const loc = isRecord(job.location) ? job.location : {};
67
67
  const country = str(loc.country);
68
- const location = str(loc.fullLocation) || [str(loc.city), str(loc.region), country.length === 2 ? country.toUpperCase() : country].filter(Boolean).join(", ") || "Unspecified";
68
+ const location = str(loc.fullLocation) || [str(loc.city), str(loc.region), countryLabel(country)].filter(Boolean).join(", ") || "Unspecified";
69
69
  const remote = loc.remote === true;
70
70
  return classifyJob({
71
71
  id: `smartrecruiters:${company.slug}:${str(job.id)}`, company: company.name, title: str(job.name), location,
@@ -168,7 +168,7 @@ const freshteam: ProviderSpec = {
168
168
  payloadVersion: () => "freshteam-widget:v1",
169
169
  normalize(company, job) {
170
170
  const branch = isRecord(job.branch) ? job.branch : {};
171
- const location = [str(branch.city), str(branch.state), str(branch.country_code).toUpperCase()].filter(Boolean).join(", ") || (job.remote === true ? "Remote" : "Unspecified");
171
+ const location = [str(branch.city), str(branch.state), countryLabel(str(branch.country_code))].filter(Boolean).join(", ") || (job.remote === true ? "Remote" : "Unspecified");
172
172
  const remote = job.remote === true;
173
173
  return classifyJob({
174
174
  id: `freshteam:${company.slug}:${str(job.unique_id) || str(job.id)}`, company: company.name, title: str(job.title), location,
@@ -196,6 +196,14 @@ export function resolveProviderSource(value: string): { ats: Ats; token: string;
196
196
  return null;
197
197
  }
198
198
 
199
+ const regionNames = new Intl.DisplayNames(["en"], { type: "region" });
200
+ /** ISO code to English name (US -> United States); anything that is not a two-letter code passes through. */
201
+ export function countryLabel(value: string): string {
202
+ const code = value.trim().toUpperCase();
203
+ if (!/^[A-Z]{2}$/.test(code)) return value.trim();
204
+ try { const name = regionNames.of(code); return name && name !== code ? name : value.trim(); } catch { return value.trim(); }
205
+ }
206
+
199
207
  function validToken(value: string | undefined): string | null {
200
208
  return value && /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value) ? value : null;
201
209
  }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.1.8";
1
+ export const VERSION = "0.1.10";