@ttctl/core 0.0.0 → 0.1.0-rc.1

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 (195) hide show
  1. package/README.md +49 -9
  2. package/dist/__generated__/gateway.d.ts +4546 -0
  3. package/dist/__generated__/gateway.d.ts.map +1 -0
  4. package/dist/__generated__/gateway.js +9 -0
  5. package/dist/__generated__/gateway.js.map +1 -0
  6. package/dist/__generated__/talent-profile-zod-schemas.d.ts +1187 -0
  7. package/dist/__generated__/talent-profile-zod-schemas.d.ts.map +1 -0
  8. package/dist/__generated__/talent-profile-zod-schemas.js +1136 -0
  9. package/dist/__generated__/talent-profile-zod-schemas.js.map +1 -0
  10. package/dist/__generated__/talent-profile.d.ts +1397 -0
  11. package/dist/__generated__/talent-profile.d.ts.map +1 -0
  12. package/dist/__generated__/talent-profile.js +9 -0
  13. package/dist/__generated__/talent-profile.js.map +1 -0
  14. package/dist/__generated__/zod-schemas.d.ts +2895 -0
  15. package/dist/__generated__/zod-schemas.d.ts.map +1 -0
  16. package/dist/__generated__/zod-schemas.js +3121 -0
  17. package/dist/__generated__/zod-schemas.js.map +1 -0
  18. package/dist/__tests__/fixtures/profile/builders.d.ts +74 -0
  19. package/dist/__tests__/fixtures/profile/builders.d.ts.map +1 -0
  20. package/dist/__tests__/fixtures/profile/builders.js +196 -0
  21. package/dist/__tests__/fixtures/profile/builders.js.map +1 -0
  22. package/dist/__tests__/fixtures/profile/data.d.ts +39 -0
  23. package/dist/__tests__/fixtures/profile/data.d.ts.map +1 -0
  24. package/dist/__tests__/fixtures/profile/data.js +230 -0
  25. package/dist/__tests__/fixtures/profile/data.js.map +1 -0
  26. package/dist/__tests__/fixtures/profile/index.d.ts +9 -0
  27. package/dist/__tests__/fixtures/profile/index.d.ts.map +1 -0
  28. package/dist/__tests__/fixtures/profile/index.js +10 -0
  29. package/dist/__tests__/fixtures/profile/index.js.map +1 -0
  30. package/dist/__tests__/fixtures/profile/types.d.ts +53 -0
  31. package/dist/__tests__/fixtures/profile/types.d.ts.map +1 -0
  32. package/dist/__tests__/fixtures/profile/types.js +4 -0
  33. package/dist/__tests__/fixtures/profile/types.js.map +1 -0
  34. package/dist/auth/errors.d.ts +82 -0
  35. package/dist/auth/errors.d.ts.map +1 -0
  36. package/dist/auth/errors.js +68 -0
  37. package/dist/auth/errors.js.map +1 -0
  38. package/dist/auth.d.ts +192 -0
  39. package/dist/auth.d.ts.map +1 -0
  40. package/dist/auth.js +294 -0
  41. package/dist/auth.js.map +1 -0
  42. package/dist/config.d.ts +212 -0
  43. package/dist/config.d.ts.map +1 -0
  44. package/dist/config.js +349 -0
  45. package/dist/config.js.map +1 -0
  46. package/dist/configLock.d.ts +50 -0
  47. package/dist/configLock.d.ts.map +1 -0
  48. package/dist/configLock.js +88 -0
  49. package/dist/configLock.js.map +1 -0
  50. package/dist/configWriter.d.ts +97 -0
  51. package/dist/configWriter.d.ts.map +1 -0
  52. package/dist/configWriter.js +687 -0
  53. package/dist/configWriter.js.map +1 -0
  54. package/dist/index.d.ts +37 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +28 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/kill-switch.d.ts +161 -0
  59. package/dist/kill-switch.d.ts.map +1 -0
  60. package/dist/kill-switch.js +235 -0
  61. package/dist/kill-switch.js.map +1 -0
  62. package/dist/lib/date.d.ts +58 -0
  63. package/dist/lib/date.d.ts.map +1 -0
  64. package/dist/lib/date.js +104 -0
  65. package/dist/lib/date.js.map +1 -0
  66. package/dist/lib/diagnostic-log.d.ts +159 -0
  67. package/dist/lib/diagnostic-log.d.ts.map +1 -0
  68. package/dist/lib/diagnostic-log.js +186 -0
  69. package/dist/lib/diagnostic-log.js.map +1 -0
  70. package/dist/lib/package-version.d.ts +19 -0
  71. package/dist/lib/package-version.d.ts.map +1 -0
  72. package/dist/lib/package-version.js +38 -0
  73. package/dist/lib/package-version.js.map +1 -0
  74. package/dist/lib/redact.d.ts +153 -0
  75. package/dist/lib/redact.d.ts.map +1 -0
  76. package/dist/lib/redact.js +207 -0
  77. package/dist/lib/redact.js.map +1 -0
  78. package/dist/lib/text.d.ts +14 -0
  79. package/dist/lib/text.d.ts.map +1 -0
  80. package/dist/lib/text.js +21 -0
  81. package/dist/lib/text.js.map +1 -0
  82. package/dist/lib/wire-shape.d.ts +131 -0
  83. package/dist/lib/wire-shape.d.ts.map +1 -0
  84. package/dist/lib/wire-shape.js +376 -0
  85. package/dist/lib/wire-shape.js.map +1 -0
  86. package/dist/onepassword.d.ts +29 -0
  87. package/dist/onepassword.d.ts.map +1 -0
  88. package/dist/onepassword.js +112 -0
  89. package/dist/onepassword.js.map +1 -0
  90. package/dist/services/_shared/transport.d.ts +148 -0
  91. package/dist/services/_shared/transport.d.ts.map +1 -0
  92. package/dist/services/_shared/transport.js +102 -0
  93. package/dist/services/_shared/transport.js.map +1 -0
  94. package/dist/services/applications/index.d.ts +210 -0
  95. package/dist/services/applications/index.d.ts.map +1 -0
  96. package/dist/services/applications/index.js +240 -0
  97. package/dist/services/applications/index.js.map +1 -0
  98. package/dist/services/availability/index.d.ts +254 -0
  99. package/dist/services/availability/index.d.ts.map +1 -0
  100. package/dist/services/availability/index.js +310 -0
  101. package/dist/services/availability/index.js.map +1 -0
  102. package/dist/services/contracts/index.d.ts +132 -0
  103. package/dist/services/contracts/index.d.ts.map +1 -0
  104. package/dist/services/contracts/index.js +211 -0
  105. package/dist/services/contracts/index.js.map +1 -0
  106. package/dist/services/engagements/index.d.ts +504 -0
  107. package/dist/services/engagements/index.d.ts.map +1 -0
  108. package/dist/services/engagements/index.js +613 -0
  109. package/dist/services/engagements/index.js.map +1 -0
  110. package/dist/services/jobs/index.d.ts +490 -0
  111. package/dist/services/jobs/index.d.ts.map +1 -0
  112. package/dist/services/jobs/index.js +753 -0
  113. package/dist/services/jobs/index.js.map +1 -0
  114. package/dist/services/payments/index.d.ts +415 -0
  115. package/dist/services/payments/index.d.ts.map +1 -0
  116. package/dist/services/payments/index.js +636 -0
  117. package/dist/services/payments/index.js.map +1 -0
  118. package/dist/services/profile/__tests__/fixtures.d.ts +214 -0
  119. package/dist/services/profile/__tests__/fixtures.d.ts.map +1 -0
  120. package/dist/services/profile/__tests__/fixtures.js +176 -0
  121. package/dist/services/profile/__tests__/fixtures.js.map +1 -0
  122. package/dist/services/profile/basic/index.d.ts +390 -0
  123. package/dist/services/profile/basic/index.d.ts.map +1 -0
  124. package/dist/services/profile/basic/index.js +1007 -0
  125. package/dist/services/profile/basic/index.js.map +1 -0
  126. package/dist/services/profile/certifications/index.d.ts +74 -0
  127. package/dist/services/profile/certifications/index.d.ts.map +1 -0
  128. package/dist/services/profile/certifications/index.js +169 -0
  129. package/dist/services/profile/certifications/index.js.map +1 -0
  130. package/dist/services/profile/education/index.d.ts +73 -0
  131. package/dist/services/profile/education/index.d.ts.map +1 -0
  132. package/dist/services/profile/education/index.js +168 -0
  133. package/dist/services/profile/education/index.js.map +1 -0
  134. package/dist/services/profile/employment/index.d.ts +111 -0
  135. package/dist/services/profile/employment/index.d.ts.map +1 -0
  136. package/dist/services/profile/employment/index.js +202 -0
  137. package/dist/services/profile/employment/index.js.map +1 -0
  138. package/dist/services/profile/external/index.d.ts +219 -0
  139. package/dist/services/profile/external/index.d.ts.map +1 -0
  140. package/dist/services/profile/external/index.js +560 -0
  141. package/dist/services/profile/external/index.js.map +1 -0
  142. package/dist/services/profile/index.d.ts +24 -0
  143. package/dist/services/profile/index.d.ts.map +1 -0
  144. package/dist/services/profile/index.js +26 -0
  145. package/dist/services/profile/index.js.map +1 -0
  146. package/dist/services/profile/industries/index.d.ts +130 -0
  147. package/dist/services/profile/industries/index.d.ts.map +1 -0
  148. package/dist/services/profile/industries/index.js +292 -0
  149. package/dist/services/profile/industries/index.js.map +1 -0
  150. package/dist/services/profile/portfolio/index.d.ts +352 -0
  151. package/dist/services/profile/portfolio/index.d.ts.map +1 -0
  152. package/dist/services/profile/portfolio/index.js +833 -0
  153. package/dist/services/profile/portfolio/index.js.map +1 -0
  154. package/dist/services/profile/resume/index.d.ts +60 -0
  155. package/dist/services/profile/resume/index.d.ts.map +1 -0
  156. package/dist/services/profile/resume/index.js +212 -0
  157. package/dist/services/profile/resume/index.js.map +1 -0
  158. package/dist/services/profile/reviews/index.d.ts +137 -0
  159. package/dist/services/profile/reviews/index.d.ts.map +1 -0
  160. package/dist/services/profile/reviews/index.js +431 -0
  161. package/dist/services/profile/reviews/index.js.map +1 -0
  162. package/dist/services/profile/shared.d.ts +127 -0
  163. package/dist/services/profile/shared.d.ts.map +1 -0
  164. package/dist/services/profile/shared.js +155 -0
  165. package/dist/services/profile/shared.js.map +1 -0
  166. package/dist/services/profile/skills/index.d.ts +212 -0
  167. package/dist/services/profile/skills/index.d.ts.map +1 -0
  168. package/dist/services/profile/skills/index.js +461 -0
  169. package/dist/services/profile/skills/index.js.map +1 -0
  170. package/dist/services/profile/visas/index.d.ts +74 -0
  171. package/dist/services/profile/visas/index.d.ts.map +1 -0
  172. package/dist/services/profile/visas/index.js +306 -0
  173. package/dist/services/profile/visas/index.js.map +1 -0
  174. package/dist/services/timesheet/index.d.ts +326 -0
  175. package/dist/services/timesheet/index.d.ts.map +1 -0
  176. package/dist/services/timesheet/index.js +324 -0
  177. package/dist/services/timesheet/index.js.map +1 -0
  178. package/dist/services/translations.d.ts +79 -0
  179. package/dist/services/translations.d.ts.map +1 -0
  180. package/dist/services/translations.js +136 -0
  181. package/dist/services/translations.js.map +1 -0
  182. package/dist/transport-resilience.d.ts +136 -0
  183. package/dist/transport-resilience.d.ts.map +1 -0
  184. package/dist/transport-resilience.js +247 -0
  185. package/dist/transport-resilience.js.map +1 -0
  186. package/dist/transport.d.ts +408 -0
  187. package/dist/transport.d.ts.map +1 -0
  188. package/dist/transport.js +691 -0
  189. package/dist/transport.js.map +1 -0
  190. package/dist/types.d.ts +41 -0
  191. package/dist/types.d.ts.map +1 -0
  192. package/dist/types.js +18 -0
  193. package/dist/types.js.map +1 -0
  194. package/package.json +40 -12
  195. package/index.js +0 -7
@@ -0,0 +1,390 @@
1
+ import type { ProfileShowQuery } from "../../../__generated__/gateway.js";
2
+ import type { DryRunPreview } from "../../../transport.js";
3
+ /**
4
+ * Profile-domain error codes. The `'UNAUTHENTICATED'` member was retired
5
+ * under issue #77 — auth-revoked failures now throw `AuthRevokedError`
6
+ * (cross-cutting `TtctlError` subclass) so the CLI / MCP surfaces can apply
7
+ * a uniform "Run `ttctl auth signin`" recovery hint regardless of which
8
+ * service raised the failure.
9
+ */
10
+ export type ProfileErrorCode = "NO_VIEWER" | "GRAPHQL_ERROR" | "NETWORK_ERROR" | "USER_ERROR" | "VALIDATION_ERROR" | "WIRE_SHAPE_ERROR" | "UNKNOWN";
11
+ export declare class ProfileError extends Error {
12
+ readonly code: ProfileErrorCode;
13
+ readonly name = "ProfileError";
14
+ constructor(code: ProfileErrorCode, message: string, options?: {
15
+ cause?: unknown;
16
+ });
17
+ }
18
+ /**
19
+ * Fetch the signed-in user's profile from the mobile-gateway GraphQL
20
+ * surface (`https://www.toptal.com/gateway/graphql/talent/graphql`).
21
+ *
22
+ * Authenticates via `Authorization: Token token=<token>` (the canonical
23
+ * Toptal auth mechanism — see `hq/engineering/adr/ADR-005-auth-model.md`).
24
+ * The mobile-gateway is plain HTTPS — no Cloudflare, no TLS impersonation
25
+ * required (empirically validated in `research/notes/13-getviewer-empirical-shape.md`).
26
+ *
27
+ * The returned shape is profile-comprehensive: identity (email, fullName,
28
+ * phoneNumber, photo), role (allocatedHours, hiredHours, availability,
29
+ * specializations, vertical, hourlyRate, timeZone, permissions, contact
30
+ * fields), profile sub-object (id, fullName, city, photo, skillSets), and
31
+ * operational metadata (codeOfConduct/termsOfService acceptance state,
32
+ * hireMeBanner, lastAllocatedHoursChangeRequest, rateInsight). Caller may
33
+ * project as needed for display.
34
+ *
35
+ * Note: `Profile.about` (bio) and `Profile.quote` (headline) are NOT on
36
+ * mobile-gateway's `Profile` type. They are write-side fields surfaced by
37
+ * `set()`'s response payload via the talent-profile surface. If a
38
+ * read-side bio/headline display becomes needed, that requires a follow-up
39
+ * issue to add a second talent-profile call.
40
+ *
41
+ * Errors:
42
+ * - `AuthRevokedError` when the surface returns 401, OR the GraphQL
43
+ * response carries `extensions.code` matching `isAuthRevokedExtensionCode`
44
+ * (`'UNAUTHENTICATED'`, `'AUTHENTICATION_REQUIRED'`, or `'UNAUTHORIZED'`
45
+ * — see `services/profile/shared.ts` for per-code surface attribution and
46
+ * empirical history; #89 added `'UNAUTHORIZED'` for mobile-gateway).
47
+ * Caller-agnostic — the CLI / MCP surfaces render `error.recovery`
48
+ * verbatim ("Run `ttctl auth signin` to re-authenticate.").
49
+ * - `ProfileError` with code `NO_VIEWER` when the response is 200 but
50
+ * `data.viewer` is `null` (the API contract says this means the token
51
+ * does not bind to a viewer).
52
+ * - `ProfileError` with code `GRAPHQL_ERROR` when the response carries a
53
+ * non-empty `errors` array (other than auth-revoked).
54
+ * - `ProfileError` with code `NETWORK_ERROR` when the transport itself
55
+ * throws (DNS, connection reset, etc).
56
+ */
57
+ export declare function show(token: string): Promise<ProfileShowQuery>;
58
+ /**
59
+ * One language entry on `Profile.languages.nodes` — identifier + display
60
+ * name. The talent_profile schema types `languages` as `Unknown` (the
61
+ * synthesized SDL marks anything the generator couldn't resolve), so the
62
+ * runtime contract is the source of truth: nodes are objects with
63
+ * non-empty string `id` and `name`. Empty / null entries are filtered
64
+ * out by {@link getBasicInfo} before the caller sees them.
65
+ */
66
+ export interface ProfileLanguage {
67
+ id: string;
68
+ name: string;
69
+ }
70
+ /**
71
+ * Read-side projection of the `talent_profile`-only profile fields that
72
+ * complement {@link show}. Returned by {@link getBasicInfo}.
73
+ *
74
+ * Naming: `bio` and `headline` are the user-facing CLI flag names exposed
75
+ * by `set()`'s {@link ProfileUpdate}, mapped to the GraphQL
76
+ * `Profile.about` / `Profile.quote` fields. We surface them as `bio` /
77
+ * `headline` here so the read and write surfaces use the same vocabulary
78
+ * — callers don't need to know the wire-side names to render the value
79
+ * the user typed.
80
+ *
81
+ * `null` indicates the user hasn't set the field (or the server didn't
82
+ * return it). `languages` is an array — empty when none are set, never
83
+ * `null` (the empty-collection convention agreed in the #124 audit's
84
+ * null-rendering recommendation).
85
+ */
86
+ export interface BasicInfo {
87
+ /** Echoes the talent_profile-side `Profile.id` (matches `show()`'s `viewerRole.profileId`). */
88
+ profileId: string;
89
+ /** The long-form bio (`Profile.about`). `null` when unset. */
90
+ bio: string | null;
91
+ /** The short tagline (`Profile.quote`). `null` when unset. */
92
+ headline: string | null;
93
+ /** User-declared languages. Empty array when none. */
94
+ languages: ProfileLanguage[];
95
+ }
96
+ /**
97
+ * Fetch the read-side `talent_profile`-only basic-info fields that
98
+ * complement {@link show} — `bio` (→ `Profile.about`), `headline` (→
99
+ * `Profile.quote`), and `languages`.
100
+ *
101
+ * Routed against `https://www.toptal.com/api/talent_profile/graphql` via
102
+ * {@link impersonatedTransport} (Cloudflare-protected; Chrome TLS
103
+ * fingerprint required). Internally calls {@link show} first to obtain
104
+ * the `profileId` required by the `profile(id: ID!)` field — same
105
+ * pattern as {@link photoShow}.
106
+ *
107
+ * Returns a typed {@link BasicInfo} projection — `null` for fields the
108
+ * user hasn't set, an empty array for `languages` when none.
109
+ *
110
+ * Errors:
111
+ * - `Cf403Error` propagates from the talent-profile transport.
112
+ * - `AuthRevokedError` on token expiry (HTTP 401, or any GraphQL
113
+ * `extensions.code` matching `isAuthRevokedExtensionCode`).
114
+ * - `ProfileError` with code `NO_VIEWER` when no viewer is bound.
115
+ * - `ProfileError` with code `USER_ERROR` when the profile id doesn't
116
+ * resolve (server returns `data.profile === null`).
117
+ * - `ProfileError` with code `GRAPHQL_ERROR` on top-level GraphQL errors
118
+ * (other than auth-revoked).
119
+ * - `ProfileError` with code `NETWORK_ERROR` on transport-level throws.
120
+ * - `ProfileError` with code `UNKNOWN` on unexpected non-2xx statuses or
121
+ * missing `data` field.
122
+ */
123
+ export declare function getBasicInfo(token: string): Promise<BasicInfo>;
124
+ /**
125
+ * Subset of profile fields editable via the wave-0 MVP write-path. `bio` and
126
+ * `headline` are the user-facing flag names exposed by the CLI; they map to
127
+ * the GraphQL fields `about` and `quote` respectively (the field names used
128
+ * by the talent_profile surface — see the response selection in
129
+ * `research/graphql/talent_profile/operations/UPDATE_BASIC_INFO.graphql`).
130
+ *
131
+ * Both fields are optional. The caller is responsible for ensuring at least
132
+ * one is supplied — `set()` rejects an empty object with a
133
+ * `VALIDATION_ERROR`.
134
+ */
135
+ export interface ProfileUpdate {
136
+ bio?: string;
137
+ headline?: string;
138
+ }
139
+ /**
140
+ * Result of a successful `set()` call. Mirrors the GraphQL field
141
+ * names so callers see `about`/`quote` rather than the CLI flag names — the
142
+ * mapping back to user-facing `bio`/`headline` is a presentation concern
143
+ * handled at the CLI layer.
144
+ */
145
+ export interface UpdateProfileResult {
146
+ profile: {
147
+ id: string;
148
+ about: string | null;
149
+ quote: string | null;
150
+ };
151
+ notice: string | null;
152
+ }
153
+ /**
154
+ * Options accepted by {@link set}. Today carries only `dryRun` (#52);
155
+ * a `confirm`-style anti-fat-finger gate is planned for `timesheet
156
+ * submit` (#13) and will follow the same option-object shape so callers
157
+ * can opt in additively without API churn.
158
+ */
159
+ export interface SetOptions {
160
+ /**
161
+ * When `true`, {@link set} short-circuits before any transport call,
162
+ * returning a {@link DryRunPreview}-bearing outcome ({@link
163
+ * SetOutcomePreview}) instead of executing the mutation. Default:
164
+ * `false` — normal apply-the-mutation path.
165
+ *
166
+ * The preview is built with placeholder substitutions for fields that
167
+ * would normally be resolved via sibling reads (e.g. `profileId`,
168
+ * which {@link set} fetches from `show()` in the apply path). Neither
169
+ * the read transport nor the write transport is invoked when `dryRun`
170
+ * is true — the AC for issue #52 is "transport never called" and the
171
+ * helper honors it for both directions of network I/O.
172
+ */
173
+ dryRun?: boolean;
174
+ }
175
+ /**
176
+ * Placeholder string substituted for fields that the apply-path would
177
+ * normally resolve via a sibling read (e.g. `profileId` from `show()`).
178
+ * Surfaced verbatim in the dry-run preview's variables payload so
179
+ * downstream consumers can see the request structure without TTCtl
180
+ * having fired any network I/O.
181
+ *
182
+ * Public (re-exported via `index.ts`) so MCP / future CLI tooling can
183
+ * recognize the placeholder when surfacing the preview.
184
+ */
185
+ export declare const DRY_RUN_PROFILE_ID_PLACEHOLDER: "<resolved at send-time from session token>";
186
+ /**
187
+ * Discriminated outcome of a {@link set} call when the apply-path
188
+ * succeeded — the server-confirmed payload normalised to {@link
189
+ * UpdateProfileResult}. Identical to the pre-#52 return type wrapped in
190
+ * a `{ kind: "applied" }` discriminator.
191
+ */
192
+ export interface SetOutcomeApplied {
193
+ kind: "applied";
194
+ result: UpdateProfileResult;
195
+ }
196
+ /**
197
+ * Discriminated outcome of a {@link set} call invoked with
198
+ * `dryRun: true` — the structured preview of the request that WOULD have
199
+ * been sent. No transport (read or write) was invoked along this path.
200
+ */
201
+ export interface SetOutcomePreview {
202
+ kind: "preview";
203
+ preview: DryRunPreview;
204
+ }
205
+ /**
206
+ * Discriminated-union return type for {@link set}. Apply-path callers
207
+ * branch on `outcome.kind === "applied"`; dry-run callers branch on
208
+ * `"preview"`. The pre-#52 surface returned `UpdateProfileResult`
209
+ * directly — that surface no longer exists. Pre-1.0 (`0.0.0`) the
210
+ * breaking change is acceptable per CLAUDE.md's "single-step migration"
211
+ * stance for the programmatic API.
212
+ */
213
+ export type SetOutcome = SetOutcomeApplied | SetOutcomePreview;
214
+ /**
215
+ * Update a subset of the signed-in user's basic-info fields (currently
216
+ * `bio` → `about` and `headline` → `quote`) via the Cloudflare-protected
217
+ * `talent_profile/graphql` surface.
218
+ *
219
+ * Authenticates via `Authorization: Token token=<token>` (the canonical
220
+ * Toptal auth mechanism). Cookies are NOT load-bearing — Chrome TLS
221
+ * impersonation alone passes Cloudflare. Internally calls `show()`
222
+ * (against mobile-gateway) first to obtain the `profileId` required by the
223
+ * mutation input, then issues the typed `UpdateBasicInfo` mutation against
224
+ * talent-profile via `impersonatedTransport`. Returns the server-confirmed
225
+ * updated values wrapped in a {@link SetOutcomeApplied} discriminator.
226
+ *
227
+ * Dry-run path (issue #52): when invoked with `options.dryRun === true`,
228
+ * builds a {@link DryRunPreview} of the WRITE request without invoking
229
+ * any transport (read OR write) and returns it wrapped in {@link
230
+ * SetOutcomePreview}. The preview substitutes a placeholder string
231
+ * ({@link DRY_RUN_PROFILE_ID_PLACEHOLDER}) for `profileId` because the
232
+ * apply-path resolves it via `show()` (a stock-transport read call) and
233
+ * the dry-run AC requires zero transport invocations. The bearer token
234
+ * is redacted in the preview's `headers.authorization` per the security
235
+ * contract documented on {@link DryRunPreview}.
236
+ *
237
+ * Errors:
238
+ * - `ProfileError` with code `VALIDATION_ERROR` when neither `bio` nor
239
+ * `headline` is supplied — the contract requires at least one. Fires
240
+ * in BOTH the apply-path and the dry-run path.
241
+ * - `Cf403Error` propagates from the talent-profile transport when
242
+ * Cloudflare returns 403. Apply-path only.
243
+ * - `AuthRevokedError` on token expiry (HTTP 401, or any GraphQL
244
+ * `extensions.code` matching `isAuthRevokedExtensionCode` — currently
245
+ * `'UNAUTHENTICATED'`, `'AUTHENTICATION_REQUIRED'`, or `'UNAUTHORIZED'`).
246
+ * Apply-path only.
247
+ * - `ProfileError` with code `NO_VIEWER` when no viewer is bound.
248
+ * Apply-path only — dry-run skips the read entirely.
249
+ * - `ProfileError` with code `USER_ERROR` when the mutation returns a
250
+ * non-empty `errors` array (validation failures from the server, e.g., a
251
+ * bio that exceeds the platform's length limit). Apply-path only.
252
+ * - `ProfileError` with code `GRAPHQL_ERROR` on top-level GraphQL errors.
253
+ * Apply-path only.
254
+ * - `ProfileError` with code `NETWORK_ERROR` on transport-level throws.
255
+ * Apply-path only.
256
+ */
257
+ export declare function set(token: string, changes: ProfileUpdate, options?: SetOptions): Promise<SetOutcome>;
258
+ /**
259
+ * `UpdatePhotoInput` shape, derived from the bundle-extracted
260
+ * `UploadProfilePhoto` mutation (`research/graphql/talent_profile/
261
+ * operations/UploadProfilePhoto.graphql`) and Pattern-1-aligned per
262
+ * `research/notes/10-mutation-input-patterns.md` (`{profileId, transformation,
263
+ * file}`).
264
+ *
265
+ * `file` is `null` in the JSON `operations` payload — the actual file
266
+ * binary travels in a separate `0` form field per the multipart-upload
267
+ * spec; the JSON placeholder is mapped onto it via the `map` form field.
268
+ *
269
+ * `transformation` carries the crop rectangle the server uses to render
270
+ * the small/cropped variants; we default to "no crop" (a 0,0 to width,height
271
+ * rectangle) when the caller doesn't supply one — server falls back to
272
+ * its own auto-crop heuristic in that case.
273
+ */
274
+ interface PhotoTransformationInput {
275
+ cropped: {
276
+ x: number;
277
+ y: number;
278
+ width: number;
279
+ height: number;
280
+ };
281
+ }
282
+ /**
283
+ * Photo URLs as exposed to consumers — mirrors the `Photo` selection set
284
+ * we ask for on the talent_profile surface. `transformations.cropped` is
285
+ * the server-recommended crop rectangle for the `small` variant; consumers
286
+ * typically use `default` for in-CLI display and `original` for export.
287
+ */
288
+ export interface PhotoUrl {
289
+ default: string | null;
290
+ original: string | null;
291
+ small: string | null;
292
+ cropped: {
293
+ x: number;
294
+ y: number;
295
+ width: number;
296
+ height: number;
297
+ } | null;
298
+ isResolutionSatisfied: boolean;
299
+ }
300
+ /**
301
+ * Fetch the URLs of the signed-in user's profile photo (default / original
302
+ * / small variants plus the server's recommended crop rectangle and the
303
+ * "is the resolution satisfactory?" boolean from `profileReadiness`).
304
+ *
305
+ * Routed against `talent_profile/graphql` via `impersonatedTransport`
306
+ * (Cloudflare-protected) because the mobile gateway exposes only a single
307
+ * flat URL on `Profile.photo` — not the variant shape we surface.
308
+ *
309
+ * Internally calls `show()` first to get the `profileId` (same pattern
310
+ * as `set()` — the talent-profile surface keys `profile(id: ID!)` rather
311
+ * than resolving from the auth token), then fires the typed query.
312
+ *
313
+ * Errors:
314
+ * - `Cf403Error` propagates from the talent-profile transport.
315
+ * - `AuthRevokedError` on token expiry (HTTP 401, or any auth-revoked
316
+ * `extensions.code` — see `isAuthRevokedExtensionCode` in
317
+ * `services/profile/shared.ts`).
318
+ * - `ProfileError` `NO_VIEWER` when no viewer is bound.
319
+ * - `ProfileError` `GRAPHQL_ERROR` on top-level GraphQL errors.
320
+ * - `ProfileError` `NETWORK_ERROR` on transport-level throws.
321
+ * - `ProfileError` `USER_ERROR` when the profile id doesn't resolve
322
+ * (server returns `data.profile === null`).
323
+ */
324
+ export declare function photoShow(token: string): Promise<PhotoUrl>;
325
+ /**
326
+ * Photo upload input. The caller supplies either a path to an image file
327
+ * (string) or an in-memory buffer. When a path is given, the helper reads
328
+ * the file via `node:fs/promises` and infers content-type from the
329
+ * extension. When a buffer is given, the caller may supply
330
+ * `contentType` and `filename` to control the multipart parts (defaults
331
+ * to `image/jpeg` and `photo.jpg`).
332
+ *
333
+ * `transformation` is optional: when omitted, the helper sends a default
334
+ * (no-crop) rectangle and lets the server's auto-crop heuristic pick.
335
+ */
336
+ export interface PhotoUploadInput {
337
+ file: Buffer | string;
338
+ filename?: string;
339
+ contentType?: string;
340
+ transformation?: PhotoTransformationInput;
341
+ }
342
+ /**
343
+ * Upload a new profile photo. Implements the GraphQL multipart-upload
344
+ * spec — the request body is a `multipart/form-data` envelope with three
345
+ * named parts (`operations`, `map`, and the file payload at field `0`),
346
+ * NOT the JSON envelope every other operation in this module uses. The
347
+ * transport hand-rolls a `node-wreq` fetch call rather than going through
348
+ * `impersonatedTransport()` because that helper hardcodes
349
+ * `Content-Type: application/json`; both transports use the same Chrome
350
+ * TLS profile so Cloudflare treats them uniformly.
351
+ *
352
+ * `input.file` accepts either a path string or a Buffer. Path strings
353
+ * are read with `node:fs/promises` and the content-type is inferred from
354
+ * the extension; Buffer callers may override `contentType` / `filename`.
355
+ *
356
+ * Errors:
357
+ * - `Cf403Error` propagates from the multipart transport call.
358
+ * - `AuthRevokedError` on token expiry (HTTP 401, or auth-revoked
359
+ * `extensions.code` on the GraphQL response).
360
+ * - `ProfileError` `VALIDATION_ERROR` when `input.file` is empty / missing.
361
+ * - `ProfileError` `NO_VIEWER` when no viewer is bound.
362
+ * - `ProfileError` `USER_ERROR` when the mutation returns user errors
363
+ * (e.g., resolution too low, file format unsupported).
364
+ * - Standard transport-error path.
365
+ */
366
+ export declare function photoUpload(token: string, input: PhotoUploadInput): Promise<PhotoUrl>;
367
+ type MultipartFetchImpl = (url: string, init: {
368
+ method: string;
369
+ headers: Record<string, string>;
370
+ body: FormData;
371
+ browser: string;
372
+ }) => Promise<{
373
+ status: number;
374
+ text: () => Promise<string>;
375
+ headers: {
376
+ toObject: () => Record<string, string>;
377
+ };
378
+ }>;
379
+ /**
380
+ * Test-only: replace the multipart-fetch implementation used by
381
+ * {@link photoUpload}. Pass `null` to restore the default. The override
382
+ * receives the same arguments as `node-wreq`'s `fetch` and must return a
383
+ * shape compatible with its `Response` (we only rely on `.status`,
384
+ * `.text()`, and `.headers.toObject()`).
385
+ *
386
+ * @internal
387
+ */
388
+ export declare function _setMultipartFetchForTesting(impl: MultipartFetchImpl | null): void;
389
+ export {};
390
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/services/profile/basic/index.ts"],"names":[],"mappings":"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"}