@hadoku/jobplatform-worker 0.0.0-stage → 2.32.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.
Files changed (117) hide show
  1. package/LICENSE +21 -0
  2. package/dist/applyTier.d.ts +33 -0
  3. package/dist/applyTier.d.ts.map +1 -0
  4. package/dist/background.d.ts +13 -0
  5. package/dist/background.d.ts.map +1 -0
  6. package/dist/clients/mailfeed.d.ts +65 -0
  7. package/dist/clients/mailfeed.d.ts.map +1 -0
  8. package/dist/clients/scraper.d.ts +61 -0
  9. package/dist/clients/scraper.d.ts.map +1 -0
  10. package/dist/commonQuestions.d.ts +35 -0
  11. package/dist/commonQuestions.d.ts.map +1 -0
  12. package/dist/defaultProfile.d.ts +29 -0
  13. package/dist/defaultProfile.d.ts.map +1 -0
  14. package/dist/errorHandler.d.ts +23 -0
  15. package/dist/errorHandler.d.ts.map +1 -0
  16. package/dist/formUrl.d.ts +17 -0
  17. package/dist/formUrl.d.ts.map +1 -0
  18. package/dist/index.d.ts +33 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +5641 -0
  21. package/dist/logger.d.ts +10 -0
  22. package/dist/logger.d.ts.map +1 -0
  23. package/dist/mailMatch.d.ts +51 -0
  24. package/dist/mailMatch.d.ts.map +1 -0
  25. package/dist/ownerName.d.ts +28 -0
  26. package/dist/ownerName.d.ts.map +1 -0
  27. package/dist/profileScore.d.ts +20 -0
  28. package/dist/profileScore.d.ts.map +1 -0
  29. package/dist/questionFamily.d.ts +57 -0
  30. package/dist/questionFamily.d.ts.map +1 -0
  31. package/dist/questionKey.d.ts +18 -0
  32. package/dist/questionKey.d.ts.map +1 -0
  33. package/dist/rank.d.ts +92 -0
  34. package/dist/rank.d.ts.map +1 -0
  35. package/dist/reconcileMail.d.ts +90 -0
  36. package/dist/reconcileMail.d.ts.map +1 -0
  37. package/dist/roleClassify.d.ts +56 -0
  38. package/dist/roleClassify.d.ts.map +1 -0
  39. package/dist/routes/companies.d.ts +12 -0
  40. package/dist/routes/companies.d.ts.map +1 -0
  41. package/dist/routes/health.d.ts +13 -0
  42. package/dist/routes/health.d.ts.map +1 -0
  43. package/dist/routes/ingest.d.ts +12 -0
  44. package/dist/routes/ingest.d.ts.map +1 -0
  45. package/dist/routes/jobs/answers.d.ts +3 -0
  46. package/dist/routes/jobs/answers.d.ts.map +1 -0
  47. package/dist/routes/jobs/applications.d.ts +4 -0
  48. package/dist/routes/jobs/applications.d.ts.map +1 -0
  49. package/dist/routes/jobs/detail.d.ts +3 -0
  50. package/dist/routes/jobs/detail.d.ts.map +1 -0
  51. package/dist/routes/jobs/feed.d.ts +3 -0
  52. package/dist/routes/jobs/feed.d.ts.map +1 -0
  53. package/dist/routes/jobs/feedback.d.ts +14 -0
  54. package/dist/routes/jobs/feedback.d.ts.map +1 -0
  55. package/dist/routes/jobs/fill.d.ts +3 -0
  56. package/dist/routes/jobs/fill.d.ts.map +1 -0
  57. package/dist/routes/jobs/index.d.ts +13 -0
  58. package/dist/routes/jobs/index.d.ts.map +1 -0
  59. package/dist/routes/jobs/owner.d.ts +41 -0
  60. package/dist/routes/jobs/owner.d.ts.map +1 -0
  61. package/dist/routes/jobs/packets.d.ts +19 -0
  62. package/dist/routes/jobs/packets.d.ts.map +1 -0
  63. package/dist/routes/jobs/preflight.d.ts +16 -0
  64. package/dist/routes/jobs/preflight.d.ts.map +1 -0
  65. package/dist/routes/jobs/shared.d.ts +140 -0
  66. package/dist/routes/jobs/shared.d.ts.map +1 -0
  67. package/dist/routes/jobs/state.d.ts +10 -0
  68. package/dist/routes/jobs/state.d.ts.map +1 -0
  69. package/dist/routes/jobs/tailoring.d.ts +3 -0
  70. package/dist/routes/jobs/tailoring.d.ts.map +1 -0
  71. package/dist/routes/jobs/verification.d.ts +22 -0
  72. package/dist/routes/jobs/verification.d.ts.map +1 -0
  73. package/dist/routes/profiles.d.ts +18 -0
  74. package/dist/routes/profiles.d.ts.map +1 -0
  75. package/dist/salaryParse.d.ts +19 -0
  76. package/dist/salaryParse.d.ts.map +1 -0
  77. package/dist/schemas.d.ts +1088 -0
  78. package/dist/schemas.d.ts.map +1 -0
  79. package/dist/scoring.d.ts +88 -0
  80. package/dist/scoring.d.ts.map +1 -0
  81. package/dist/slugParse.d.ts +24 -0
  82. package/dist/slugParse.d.ts.map +1 -0
  83. package/dist/types.d.ts +59 -0
  84. package/dist/types.d.ts.map +1 -0
  85. package/dist/userId.d.ts +40 -0
  86. package/dist/userId.d.ts.map +1 -0
  87. package/dist/verificationCode.d.ts +27 -0
  88. package/dist/verificationCode.d.ts.map +1 -0
  89. package/migrations/0001_init.sql +52 -0
  90. package/migrations/0002_user_companies.sql +21 -0
  91. package/migrations/0003_jobs_ats_slug.sql +10 -0
  92. package/migrations/0004_profiles_user_id.sql +25 -0
  93. package/migrations/0005_job_states.sql +34 -0
  94. package/migrations/0006_default_profile.sql +24 -0
  95. package/migrations/0007_profile_companies.sql +20 -0
  96. package/migrations/0008_drop_job_profile_matches.sql +8 -0
  97. package/migrations/0009_role_track_level.sql +63 -0
  98. package/migrations/0010_job_states_variant_slug.sql +7 -0
  99. package/migrations/0011_scoring_prefs.sql +7 -0
  100. package/migrations/0012_job_feedback.sql +16 -0
  101. package/migrations/0013_last_seen_at.sql +6 -0
  102. package/migrations/0014_applications.sql +31 -0
  103. package/migrations/0015_applications_approved_fingerprint.sql +21 -0
  104. package/migrations/0016_applications_job_closed.sql +43 -0
  105. package/migrations/0017_application_answers.sql +32 -0
  106. package/migrations/0018_jobs_slug_source.sql +24 -0
  107. package/migrations/0019_index_maintenance.sql +41 -0
  108. package/migrations/0020_job_profile_rank.sql +55 -0
  109. package/migrations/0021_applications_mail_reconciliation.sql +72 -0
  110. package/migrations/0022_mail_reconcile_owner_scope.sql +45 -0
  111. package/migrations/0023_applicant_profiles.sql +14 -0
  112. package/migrations/0024_owner_name_cache.sql +14 -0
  113. package/migrations/0025_drop_freshness_index.sql +15 -0
  114. package/migrations/0026_drop_user_companies.sql +6 -0
  115. package/migrations/0027_job_profile_rank_by_job.sql +16 -0
  116. package/package.json +24 -4
  117. package/README.md +0 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matthaeus Wolff
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Can the form runner drive this posting's application, and do we KNOW it can?
3
+ *
4
+ * Two different claims, kept apart on purpose.
5
+ *
6
+ * `apply_tier` is a PREDICTION read off the URL. It says an adapter exists for
7
+ * this shape of posting, nothing more — no page has been opened.
8
+ *
9
+ * `apply_verified` is EVIDENCE: a form on this same board has actually been
10
+ * filled by the runner. That is the only thing entitled to read as "we checked".
11
+ *
12
+ * Collapsing the two would put a confident badge on a guess, which is the
13
+ * failure this whole subsystem keeps having to unlearn.
14
+ *
15
+ * KEEP IN STEP WITH the adapter table in hadoku-scrape:
16
+ * `hadoku_scrape/apply/runner.py::ADAPTERS`. That module is the source of
17
+ * truth — it is what actually drives the browser — and this is a copy for the
18
+ * UI, because the two live in different languages and different processes.
19
+ */
20
+ /**
21
+ * - `supported` a proven adapter matches this host outright.
22
+ * - `embedded` the employer serves its own careers page, but the form
23
+ * underneath is Greenhouse and is reachable at that board's
24
+ * embed URL. Verified 2026-09-01 against Coinbase: the
25
+ * canonical board URL 302s TO the company domain, while
26
+ * `job-boards.greenhouse.io/embed/job_app?for=…&token=…` still
27
+ * serves the real form, and the Greenhouse adapter filled it.
28
+ * - `unsupported` a real ATS we have no adapter for. Honest "no", not "unknown".
29
+ * - `unknown` nothing recognisable. No claim either way.
30
+ */
31
+ export type ApplyTier = 'supported' | 'embedded' | 'unsupported' | 'unknown';
32
+ export declare function applyTier(applicationUrl: string | null, ats: string | null): ApplyTier;
33
+ //# sourceMappingURL=applyTier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applyTier.d.ts","sourceRoot":"","sources":["../src/applyTier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,aAAa,GAAG,SAAS,CAAC;AAkC7E,wBAAgB,SAAS,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAuBtF"}
@@ -0,0 +1,13 @@
1
+ export interface HasExecutionCtx {
2
+ executionCtx?: {
3
+ waitUntil(p: Promise<unknown>): void;
4
+ };
5
+ }
6
+ /**
7
+ * Run `work` after the response.
8
+ *
9
+ * `label` names the work in logs — it is what tells you WHICH background task
10
+ * was dropped when a host stops forwarding its ExecutionContext.
11
+ */
12
+ export declare function runAfterResponse(c: HasExecutionCtx, label: string, work: Promise<unknown>): void;
13
+ //# sourceMappingURL=background.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"background.d.ts","sourceRoot":"","sources":["../src/background.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,eAAe;IAC/B,YAAY,CAAC,EAAE;QAAE,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAA;KAAE,CAAC;CACxD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAwBhG"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Client for contact-api's mail feed — ATS mail addressed to the owner.
3
+ *
4
+ * Auth: `X-User-Key: ${env.HADOKU_SERVICE_KEY}`, the service identity this worker
5
+ * already holds. We carry NO mail credential: contact-api resolves the key to a
6
+ * userId and looks that up in a grant table naming our sender domains.
7
+ * Everything else in the mailbox is invisible to us, enforced in SQL rather
8
+ * than filtered after the fetch. Service tier alone is not enough — every
9
+ * worker key in the fleet has that and they all get 403.
10
+ *
11
+ * See docs/RESPONSE-application-mail-access.md for the contract.
12
+ */
13
+ import type { AppEnv } from '../types.js';
14
+ /** One message. `body` is fetched but deliberately never persisted. */
15
+ export interface MailMessage {
16
+ id: string;
17
+ receivedAt: number;
18
+ from: string;
19
+ fromDomain: string;
20
+ subject: string;
21
+ body: string;
22
+ source: 'live' | 'archive';
23
+ }
24
+ export interface MailPage {
25
+ messages: MailMessage[];
26
+ nextCursor: string | null;
27
+ }
28
+ export interface MailScope {
29
+ label: string;
30
+ senderDomains: string[];
31
+ }
32
+ /**
33
+ * The sender domains we are actually granted.
34
+ *
35
+ * Asserted on every run, and not as a formality. An empty page and a grant that
36
+ * silently lost a domain look identical from `/messages`, and detecting silence
37
+ * is this feature's entire job. This is also precisely how the `greenhouse.io`
38
+ * error would have been caught before it shipped: the grant would have listed a
39
+ * domain that has never delivered anything.
40
+ */
41
+ export declare function fetchScope(env: AppEnv): Promise<MailScope | {
42
+ error: string;
43
+ }>;
44
+ /**
45
+ * One page of mail, oldest first.
46
+ *
47
+ * Ascending `(created_at, id)` across the live table and the archive both, so
48
+ * the FIRST call with no cursor is the backfill and the same loop tomorrow from
49
+ * the stored cursor is the incremental poll. There is deliberately no separate
50
+ * import path: a second path is a second implementation of the same matching
51
+ * rules, and that drift is what this shape prevents.
52
+ */
53
+ export declare function fetchMessages(env: AppEnv, opts?: {
54
+ cursor?: string | null;
55
+ limit?: number;
56
+ since?: string;
57
+ }): Promise<MailPage | {
58
+ error: string;
59
+ }>;
60
+ export declare function isFeedError<T>(value: T | {
61
+ error: string;
62
+ }): value is {
63
+ error: string;
64
+ };
65
+ //# sourceMappingURL=mailfeed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mailfeed.d.ts","sourceRoot":"","sources":["../../src/clients/mailfeed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI1C,uEAAuE;AACvE,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,MAAM,WAAW,QAAQ;IACxB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,SAAS;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAsCD;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAEpF;AAED;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CAClC,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GACnE,OAAO,CAAC,QAAQ,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAMvC;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,IAAI;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAEvF"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Typed client for hadoku-scrape `/api/v1/jobboards/*` endpoints.
3
+ *
4
+ * Auth: `X-User-Key: ${env.HADOKU_SERVICE_KEY}` — service-tier key bound from
5
+ * vault JOBPLATFORM_SCRAPER_KEY. Bearer was deprecated 2026-05-05 (scraper
6
+ * backend rejects Bearer; only X-User-Key is accepted post-PR1). Push key
7
+ * via `python scripts/administration.py cloudflare-secrets jobplatform-api`.
8
+ *
9
+ * OpenAPI source of truth: https://scraper.hadoku.me/openapi.json
10
+ */
11
+ import type { AppEnv } from '../types.js';
12
+ /** One provider's answer for a probed slug. Only greenhouse exposes a name. */
13
+ interface ProviderHit {
14
+ ats: string;
15
+ company_name: string | null;
16
+ n_jobs: number;
17
+ sample_titles: string[];
18
+ }
19
+ export interface SlugProbeResult {
20
+ slug: string;
21
+ hits: ProviderHit[];
22
+ }
23
+ /** The single best board matched to a typed company name (most open jobs). */
24
+ export interface CompanyMatch {
25
+ query: string;
26
+ matched: boolean;
27
+ ats: string | null;
28
+ slug: string | null;
29
+ company_name: string | null;
30
+ n_jobs: number;
31
+ sample_titles: string[];
32
+ domain: string | null;
33
+ }
34
+ export declare class ScraperClientError extends Error {
35
+ readonly status: number;
36
+ readonly body: string;
37
+ constructor(message: string, status: number, body: string);
38
+ }
39
+ /**
40
+ * Probe explicit slugs against each ATS. Read-only — reports the company name
41
+ * (greenhouse only), open-job count, and sample titles per provider so the
42
+ * operator can confirm the correct (ats, slug) before locking it in. Does NOT
43
+ * register anything.
44
+ */
45
+ export declare function probeSlugs(env: AppEnv, slugs: string[], providers?: string[]): Promise<SlugProbeResult[]>;
46
+ /**
47
+ * Match company names to the single best board each (most open jobs). Read-only.
48
+ * Powers the name-driven prefetch: the user types "Scale AI" and gets
49
+ * greenhouse:scaleai + display name + job count to confirm before locking.
50
+ */
51
+ export declare function matchCompanies(env: AppEnv, names: string[], providers?: string[]): Promise<CompanyMatch[]>;
52
+ /**
53
+ * Trigger a scrape across all active registry targets. Fire-and-forget (202).
54
+ *
55
+ * Note: scraper has no per-target /search filter, so calling this after adding
56
+ * one company runs the full active registry. Acceptable for a small registry;
57
+ * worth revisiting if registry grows.
58
+ */
59
+ export declare function triggerSearch(env: AppEnv): Promise<void>;
60
+ export {};
61
+ //# sourceMappingURL=scraper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scraper.d.ts","sourceRoot":"","sources":["../../src/clients/scraper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI1C,+EAA+E;AAC/E,UAAU,WAAW;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,EAAE,CAAC;CACpB;AAQD,8EAA8E;AAC9E,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAWD,qBAAa,kBAAmB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAMzD;AA0BD;;;;;GAKG;AACH,wBAAsB,UAAU,CAC/B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EAAE,EACf,SAAS,CAAC,EAAE,MAAM,EAAE,GAClB,OAAO,CAAC,eAAe,EAAE,CAAC,CAqB5B;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CACnC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EAAE,EACf,SAAS,CAAC,EAAE,MAAM,EAAE,GAClB,OAAO,CAAC,YAAY,EAAE,CAAC,CAqBzB;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAY9D"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The questions worth answering before a board ever asks them.
3
+ *
4
+ * MEASURED, not guessed: sampled from 26 live Greenhouse application forms on
5
+ * 2026-09-01 by reading every question label and counting how many boards asked
6
+ * each. The head of that distribution is short and steep, which is what makes
7
+ * seeding worth doing — a handful of answers covers most forms.
8
+ *
9
+ * Identity fields the runner already fills from the profile (name, email,
10
+ * phone, résumé, current company) are excluded; nobody needs to answer those
11
+ * twice.
12
+ *
13
+ * Notes that would once have been rendered as UI copy live here instead:
14
+ *
15
+ * - "Location (City)" wants a bare city. A full "City, State, Country" is a
16
+ * different answer, and this is the field that actually stopped a real
17
+ * Coinbase fill.
18
+ * - "Preferred First Name" is never inferred from a legal name.
19
+ * - The two work-authorization entries are genuinely different questions.
20
+ * Matching refuses to read "the country where the job is located" as "the
21
+ * United States", so both need their own answer.
22
+ * - "Agreement to Arbitrate" is a binding legal agreement. Storing a standing
23
+ * yes applies it to every future application at that employer.
24
+ *
25
+ * These are observations with a date on them, not constants. Re-sample if the
26
+ * shape starts looking wrong.
27
+ */
28
+ export interface CommonQuestion {
29
+ question: string;
30
+ /** How many of the sampled boards asked it. */
31
+ seenOn: number;
32
+ }
33
+ export declare const SAMPLE_SIZE = 26;
34
+ export declare const COMMON_QUESTIONS: CommonQuestion[];
35
+ //# sourceMappingURL=commonQuestions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commonQuestions.d.ts","sourceRoot":"","sources":["../src/commonQuestions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,WAAW,KAAK,CAAC;AAE9B,eAAO,MAAM,gBAAgB,EAAE,cAAc,EAoB5C,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The shared default profile — the starting "hiring slice" every user gets.
3
+ *
4
+ * Seeded from code. On a user's first GET /profiles it's materialized into a
5
+ * real per-user `profiles` row (flagged is_default=1) plus its seed companies,
6
+ * so it participates in scoring and can own companies like any profile. Editing
7
+ * it just updates that row; deleting it writes a tombstone so it isn't
8
+ * re-created. Bump these values to change what NEW users start from; it does
9
+ * not retroactively change users who already have the row.
10
+ */
11
+ import type { RoleLevel } from './roleClassify.js';
12
+ import type { ProfileTrack } from './profileScore.js';
13
+ export interface DefaultProfileSeed {
14
+ name: string;
15
+ keywords: string[];
16
+ track: ProfileTrack;
17
+ levels: RoleLevel[];
18
+ remote_pref: 'remote' | 'hybrid' | 'onsite' | 'any';
19
+ }
20
+ export declare const DEFAULT_PROFILE: DefaultProfileSeed;
21
+ export interface DefaultCompany {
22
+ ats: string;
23
+ slug: string;
24
+ display_name: string;
25
+ }
26
+ /** Companies the default profile ships with — a useful starter set of real,
27
+ * currently-hiring boards so the profile works out of the box. */
28
+ export declare const DEFAULT_PROFILE_COMPANIES: DefaultCompany[];
29
+ //# sourceMappingURL=defaultProfile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaultProfile.d.ts","sourceRoot":"","sources":["../src/defaultProfile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,WAAW,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;CACpD;AAED,eAAO,MAAM,eAAe,EAAE,kBAS7B,CAAC;AAEF,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACrB;AAED;kEACkE;AAClE,eAAO,MAAM,yBAAyB,EAAE,cAAc,EAUrD,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The last-resort handler for an exception no route caught.
3
+ *
4
+ * It replaces worker-utils' generic one for two reasons, both learned from
5
+ * issue #24 (43 ingest batches refused in the opening seconds of the
6
+ * 2026-10-20 scrape):
7
+ *
8
+ * 1. The generic handler logs `console.error('Unhandled error:', err)`, and
9
+ * Workers Logs records a D1 error as its stack with the first line gone, so
10
+ * every one of those 43 entries read `Unhandled error: at
11
+ * D1DatabaseSessionAlwaysPrimary._sendOrThrow ...`. That is a stack with no
12
+ * message. The message and cause are logged here as their own fields.
13
+ *
14
+ * 2. Every failure answered 500 with the same body, so the scraper could not
15
+ * tell "D1 shed load for a few seconds" from "this batch is bad". The
16
+ * failures that day were D1 refusing within 10-70ms while four other
17
+ * batches held it for ~9s each. They were transient, and a transient D1
18
+ * failure now answers 503 with Retry-After.
19
+ */
20
+ import type { Context } from 'hono';
21
+ export declare function isTransientD1Error(err: unknown): boolean;
22
+ export declare function errorHandler(err: Error, c: Context): Response;
23
+ //# sourceMappingURL=errorHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../src/errorHandler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AA2BpC,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAExD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,GAAG,QAAQ,CA4B7D"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The page a person fills to apply for this job — what the dashboard's
3
+ * "Open form" links to, and where the Fill extension runs.
4
+ *
5
+ * Built from the board and slug the scraper recorded rather than the posting
6
+ * URL: a Greenhouse posting is often published on the employer's own careers
7
+ * page (samsara.com/…?gh_jid=), which is a wrapper the extension cannot see
8
+ * into reliably. The embed form is the same application, served by Greenhouse.
9
+ * Mirrors the runner's GreenhouseFiller.rewrite_url and its Ashby/Lever URLs.
10
+ */
11
+ export declare function formUrl(job: {
12
+ id: string;
13
+ ats: string | null;
14
+ slug: string | null;
15
+ url: string;
16
+ }): string;
17
+ //# sourceMappingURL=formUrl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formUrl.d.ts","sourceRoot":"","sources":["../src/formUrl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;CACZ,GAAG,MAAM,CAYT"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Job Platform Worker
3
+ *
4
+ * Exports a Hono handler factory consumed by hadoku_site's host worker.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * // hadoku_site/workers/jobplatform-api/src/index.ts
9
+ * import { createJobPlatformHandler } from '@wolffm/jobplatform-worker';
10
+ *
11
+ * export default {
12
+ * fetch(request: Request, env: Env) {
13
+ * return createJobPlatformHandler('/jobplatform/api').fetch(request, env);
14
+ * },
15
+ * };
16
+ * ```
17
+ */
18
+ import { OpenAPIHono } from '@hono/zod-openapi';
19
+ import { type HadokuAuthContext } from '@wolffm/worker-utils';
20
+ import type { ExecutionContext } from '@cloudflare/workers-types';
21
+ import type { AppEnv } from './types.js';
22
+ interface AppContext {
23
+ Bindings: AppEnv;
24
+ Variables: {
25
+ authContext: HadokuAuthContext;
26
+ };
27
+ }
28
+ export declare function createJobPlatformHandler(basePath?: string): OpenAPIHono<AppContext, {}, string>;
29
+ export declare function createFetchHandler(env: AppEnv): (request: Request, ctx?: ExecutionContext) => Response | Promise<Response>;
30
+ export declare function createScheduledHandler(_env: AppEnv): (cron: string) => void;
31
+ export type { AppEnv } from './types.js';
32
+ export * from './schemas.js';
33
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAMN,KAAK,iBAAiB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AASzC,UAAU,UAAU;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE;QAAE,WAAW,EAAE,iBAAiB,CAAA;KAAE,CAAC;CAC9C;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,SAAqB,uCAoDrE;AAYD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,IAErC,SAAS,OAAO,EAAE,MAAM,gBAAgB,kCAChD;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,IAC1C,MAAM,MAAM,UAGpB;AAED,YAAY,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,cAAc,cAAc,CAAC"}