@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,306 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { AuthRevokedError, TtctlError } from "../../../auth/errors.js";
4
+ import { impersonatedTransport } from "../../../transport.js";
5
+ import { extractProfileId, isAuthRevokedExtensionCode } from "../shared.js";
6
+ export class VisasError extends Error {
7
+ code;
8
+ name = "VisasError";
9
+ constructor(code, message, options) {
10
+ super(message, options);
11
+ this.code = code;
12
+ }
13
+ }
14
+ /** Map a server `TravelVisa` node to the typed {@link TravelVisa}. */
15
+ function mapTravelVisaNode(node) {
16
+ const country = (node["country"] ?? {});
17
+ const id = node["id"];
18
+ return {
19
+ id: typeof id === "string" ? id : "",
20
+ countryId: country.id ?? "",
21
+ countryName: country.name ?? "",
22
+ visaType: node["visaType"] ?? "",
23
+ expiryDate: node["expiryDate"] ?? null,
24
+ };
25
+ }
26
+ /**
27
+ * Common "200 with errors" shape handler. Returns the unwrapped payload
28
+ * (the value of the single root data field) as `unknown`; callers
29
+ * narrow to their per-operation payload shape at the call site.
30
+ */
31
+ function unwrapResponse(res, operationName) {
32
+ if (res.status === 401) {
33
+ throw new AuthRevokedError("Session is invalid or expired.");
34
+ }
35
+ if (res.status < 200 || res.status >= 300) {
36
+ throw new VisasError("UNKNOWN", `${operationName} returned HTTP ${res.status.toString()}`);
37
+ }
38
+ const body = res.body;
39
+ if (body && Array.isArray(body.errors) && body.errors.length > 0) {
40
+ const first = body.errors[0];
41
+ if (isAuthRevokedExtensionCode(first?.extensions?.code)) {
42
+ throw new AuthRevokedError("Session is invalid or expired.");
43
+ }
44
+ throw new VisasError("GRAPHQL_ERROR", `${operationName} failed: ${first?.message ?? "GraphQL error"}`);
45
+ }
46
+ if (!body?.data) {
47
+ throw new VisasError("UNKNOWN", `${operationName} response had no \`data\` field`);
48
+ }
49
+ const keys = Object.keys(body.data);
50
+ if (keys.length === 0) {
51
+ throw new VisasError("UNKNOWN", `${operationName} response had empty \`data\``);
52
+ }
53
+ const firstKey = keys[0];
54
+ const payload = body.data[firstKey];
55
+ if (payload === null || payload === undefined) {
56
+ throw new VisasError("UNKNOWN", `${operationName} response had \`null\` payload`);
57
+ }
58
+ return payload;
59
+ }
60
+ function rejectIfUserErrors(errors, operationName) {
61
+ if (Array.isArray(errors) && errors.length > 0) {
62
+ const first = errors[0];
63
+ const fieldHint = first?.key ? ` (${first.key})` : "";
64
+ throw new VisasError("USER_ERROR", `${operationName} rejected${fieldHint}: ${first?.message ?? "unknown error"}`);
65
+ }
66
+ }
67
+ async function withTransportErrors(operationName, fn) {
68
+ try {
69
+ return await fn();
70
+ }
71
+ catch (err) {
72
+ if (err instanceof TtctlError)
73
+ throw err;
74
+ if (err instanceof VisasError)
75
+ throw err;
76
+ throw new VisasError("NETWORK_ERROR", `${operationName} request failed: ${err.message}`, {
77
+ cause: err,
78
+ });
79
+ }
80
+ }
81
+ /* -------------------------------------------------------------------------- */
82
+ /* Operations */
83
+ /* -------------------------------------------------------------------------- */
84
+ /**
85
+ * Full-document `getTravelVisas` query (talent-profile).
86
+ *
87
+ * The `Profile.travelVisas` field is only defined on the `talent-profile`
88
+ * schema — `mobile-gateway` does not expose it (verified against
89
+ * `research/graphql/talent_profile/operations/getTravelVisas.graphql` and
90
+ * the synthesized SDLs under `research/graphql/`). Sibling mutations
91
+ * (`createTravelVisa`, `updateTravelVisa`, `removeTravelVisa`) already
92
+ * route through `talent-profile` for the same reason; this read aligns
93
+ * with that surface choice.
94
+ */
95
+ const GET_TRAVEL_VISAS_QUERY = `query getTravelVisas($profileId: ID!) {
96
+ profile(id: $profileId) {
97
+ id
98
+ travelVisas {
99
+ nodes {
100
+ id
101
+ country {
102
+ id
103
+ name
104
+ }
105
+ visaType
106
+ expiryDate
107
+ }
108
+ }
109
+ }
110
+ }`;
111
+ /**
112
+ * Fetch the signed-in user's travel-visa records. Empty list returns `[]`,
113
+ * never `null`.
114
+ */
115
+ export async function list(token) {
116
+ const profileId = await extractProfileId(token);
117
+ const res = await withTransportErrors("getTravelVisas", async () => impersonatedTransport({
118
+ surface: "talent-profile",
119
+ authToken: token,
120
+ body: {
121
+ operationName: "getTravelVisas",
122
+ query: GET_TRAVEL_VISAS_QUERY,
123
+ variables: { profileId },
124
+ },
125
+ }));
126
+ const profile = unwrapResponse(res, "getTravelVisas");
127
+ return profile.travelVisas.nodes.map(mapTravelVisaNode);
128
+ }
129
+ const CREATE_TRAVEL_VISA_MUTATION = `mutation createTravelVisa($input: CreateTravelVisaInput!) {
130
+ createTravelVisa(input: $input) {
131
+ profile {
132
+ id
133
+ travelVisas {
134
+ nodes {
135
+ id
136
+ country {
137
+ id
138
+ name
139
+ }
140
+ visaType
141
+ expiryDate
142
+ }
143
+ }
144
+ }
145
+ errors {
146
+ code
147
+ key
148
+ message
149
+ }
150
+ }
151
+ }`;
152
+ /**
153
+ * Create a new travel-visa record.
154
+ *
155
+ * Both `countryId` and `visaType` are required by the AC; `expiryDate`
156
+ * is optional. The wire-side error path returns `errors[]` on the
157
+ * mutation payload when the server rejects (e.g. unknown country,
158
+ * malformed date) — those collapse into `USER_ERROR`.
159
+ */
160
+ export async function add(token, input) {
161
+ if (!input.countryId || input.countryId.trim() === "") {
162
+ throw new VisasError("VALIDATION_ERROR", "Travel visa requires a non-empty `countryId`.");
163
+ }
164
+ if (!input.visaType || input.visaType.trim() === "") {
165
+ throw new VisasError("VALIDATION_ERROR", "Travel visa requires a non-empty `visaType`.");
166
+ }
167
+ const profileId = await extractProfileId(token);
168
+ const variables = {
169
+ input: { profileId, travelVisa: input },
170
+ };
171
+ const res = await withTransportErrors("createTravelVisa", async () => impersonatedTransport({
172
+ surface: "talent-profile",
173
+ authToken: token,
174
+ body: {
175
+ operationName: "createTravelVisa",
176
+ query: CREATE_TRAVEL_VISA_MUTATION,
177
+ variables,
178
+ },
179
+ }));
180
+ const payload = unwrapResponse(res, "createTravelVisa");
181
+ rejectIfUserErrors(payload.errors, "createTravelVisa");
182
+ if (!payload.profile) {
183
+ throw new VisasError("UNKNOWN", "createTravelVisa succeeded but response had no profile payload.");
184
+ }
185
+ return payload.profile.travelVisas.nodes.map(mapTravelVisaNode);
186
+ }
187
+ const UPDATE_TRAVEL_VISA_MUTATION = `mutation updateTravelVisa($input: UpdateTravelVisaInput!) {
188
+ updateTravelVisa(input: $input) {
189
+ profile {
190
+ id
191
+ travelVisas {
192
+ nodes {
193
+ id
194
+ country {
195
+ id
196
+ name
197
+ }
198
+ visaType
199
+ expiryDate
200
+ }
201
+ }
202
+ }
203
+ errors {
204
+ code
205
+ key
206
+ message
207
+ }
208
+ }
209
+ }`;
210
+ /**
211
+ * Update a travel-visa record by id.
212
+ *
213
+ * `UpdateTravelVisaInput.travelVisa` is treated as a **full-document
214
+ * replacement** by the server — `countryId` and `visaType` are
215
+ * non-nullable on the input even when the caller doesn't want to change
216
+ * them. Sending only the patched field returns
217
+ * `Variable $input of type UpdateTravelVisaInput! was provided invalid
218
+ * value for travelVisa.countryId/visaType (Expected value to not be
219
+ * null)` (verified live 2026-05-15, see #317).
220
+ *
221
+ * To preserve the partial-update UX over the full-replace wire shape,
222
+ * this function does read-modify-write: fetch the current record via
223
+ * {@link list}, merge caller's `changes` on top of the current state,
224
+ * then send the merged input. Same pattern as
225
+ * `services/profile/portfolio/index.ts` `update` (PR #323 / #314).
226
+ */
227
+ export async function update(token, id, changes) {
228
+ if (Object.keys(changes).length === 0) {
229
+ throw new VisasError("VALIDATION_ERROR", "Travel visa update requires at least one field.");
230
+ }
231
+ // Read-modify-write: see function doc-comment.
232
+ const current = (await list(token)).find((v) => v.id === id);
233
+ if (!current) {
234
+ throw new VisasError("VALIDATION_ERROR", `Travel visa ${id} not found.`);
235
+ }
236
+ // `expiryDate` is `string | null` on the read side but `string | undefined`
237
+ // on the write side; the conditional spread satisfies
238
+ // `exactOptionalPropertyTypes: true` (omit when null, value when string).
239
+ const merged = {
240
+ countryId: current.countryId,
241
+ visaType: current.visaType,
242
+ ...(current.expiryDate !== null && { expiryDate: current.expiryDate }),
243
+ ...changes,
244
+ };
245
+ const variables = {
246
+ input: { travelVisaId: id, travelVisa: merged },
247
+ };
248
+ const res = await withTransportErrors("updateTravelVisa", async () => impersonatedTransport({
249
+ surface: "talent-profile",
250
+ authToken: token,
251
+ body: {
252
+ operationName: "updateTravelVisa",
253
+ query: UPDATE_TRAVEL_VISA_MUTATION,
254
+ variables,
255
+ },
256
+ }));
257
+ const payload = unwrapResponse(res, "updateTravelVisa");
258
+ rejectIfUserErrors(payload.errors, "updateTravelVisa");
259
+ if (!payload.profile) {
260
+ throw new VisasError("UNKNOWN", "updateTravelVisa succeeded but response had no profile payload.");
261
+ }
262
+ return payload.profile.travelVisas.nodes.map(mapTravelVisaNode);
263
+ }
264
+ const REMOVE_TRAVEL_VISA_MUTATION = `mutation removeTravelVisa($input: RemoveTravelVisaInput!) {
265
+ removeTravelVisa(input: $input) {
266
+ profile {
267
+ id
268
+ travelVisas {
269
+ nodes {
270
+ id
271
+ country {
272
+ id
273
+ name
274
+ }
275
+ visaType
276
+ expiryDate
277
+ }
278
+ }
279
+ }
280
+ errors {
281
+ code
282
+ key
283
+ message
284
+ }
285
+ }
286
+ }`;
287
+ /** Remove a travel-visa record by id. */
288
+ export async function remove(token, id) {
289
+ const variables = { input: { travelVisaId: id } };
290
+ const res = await withTransportErrors("removeTravelVisa", async () => impersonatedTransport({
291
+ surface: "talent-profile",
292
+ authToken: token,
293
+ body: {
294
+ operationName: "removeTravelVisa",
295
+ query: REMOVE_TRAVEL_VISA_MUTATION,
296
+ variables,
297
+ },
298
+ }));
299
+ const payload = unwrapResponse(res, "removeTravelVisa");
300
+ rejectIfUserErrors(payload.errors, "removeTravelVisa");
301
+ if (!payload.profile) {
302
+ throw new VisasError("UNKNOWN", "removeTravelVisa succeeded but response had no profile payload.");
303
+ }
304
+ return payload.profile.travelVisas.nodes.map(mapTravelVisaNode);
305
+ }
306
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/services/profile/visas/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,326 @@
1
+ import type { DryRunPreview } from "../../transport.js";
2
+ /**
3
+ * Timesheet-domain error codes. Mirrors the `EngagementsError` /
4
+ * `ApplicationsError` shape per project convention.
5
+ *
6
+ * - `NO_VIEWER`: HTTP 200 + `data.viewer === null` (defensive — should
7
+ * never happen with a valid token).
8
+ * - `NOT_FOUND`: the supplied id (billing cycle or engagement) does
9
+ * not resolve. The wire shape can surface this either as a top-level
10
+ * `Record not found` GraphQL error OR as `data.node === null` /
11
+ * `data.viewer.jobActivityItem === null`.
12
+ * - `NO_ENGAGEMENT`: the activity row exists but has no engagement
13
+ * (e.g., an interview-only row that never became an engagement). The
14
+ * timesheet domain can't operate on such rows.
15
+ * - `NO_CURRENT_CYCLE`: {@link resolveCurrentCycle} returned zero
16
+ * matches — used by the CLI's submit auto-resolve path so callers
17
+ * can distinguish "nothing to submit" from "id was invalid".
18
+ * - `MULTIPLE_CURRENT_CYCLES`: {@link resolveCurrentCycle} returned
19
+ * more than one match — used by the CLI's submit auto-resolve path
20
+ * so callers can prompt the user to disambiguate. The error carries
21
+ * the candidate list on `EngagementsError.cause` (not exposed via
22
+ * typing; callers use `resolveCurrentCycle` directly when they need
23
+ * structured access).
24
+ * - `GRAPHQL_ERROR`: top-level `errors[]` from the gateway, not an
25
+ * auth-revoked extension and not a `Record not found`.
26
+ * - `MUTATION_ERROR`: the `MutationResult.errors[]` payload from
27
+ * `SubmitTimesheet` (the wire operation succeeded at GraphQL level,
28
+ * but the submission itself reports per-field errors — overdue,
29
+ * missing required hours, etc.).
30
+ * - `NETWORK_ERROR`, `UNKNOWN`: standard transport failure modes.
31
+ *
32
+ * Auth-revoked failures throw `AuthRevokedError` (cross-cutting
33
+ * `TtctlError` subclass per #77), not a code on this enum.
34
+ */
35
+ export type TimesheetErrorCode = "NO_VIEWER" | "NOT_FOUND" | "NO_ENGAGEMENT" | "NO_CURRENT_CYCLE" | "MULTIPLE_CURRENT_CYCLES" | "GRAPHQL_ERROR" | "MUTATION_ERROR" | "NETWORK_ERROR" | "WIRE_SHAPE_ERROR" | "UNKNOWN";
36
+ export declare class TimesheetError extends Error {
37
+ readonly code: TimesheetErrorCode;
38
+ readonly name = "TimesheetError";
39
+ constructor(code: TimesheetErrorCode, message: string, options?: {
40
+ cause?: unknown;
41
+ });
42
+ }
43
+ /**
44
+ * Minimum-commitment payload mirrored from the captured
45
+ * `minimumCommitmentData` fragment. When `applicable === false`, the
46
+ * server explains via `reasonNotApplicable`.
47
+ */
48
+ export interface TimesheetMinimumCommitment {
49
+ applicable: boolean;
50
+ minimumHours: number | null;
51
+ reasonNotApplicable: string | null;
52
+ }
53
+ /**
54
+ * Reference to the engagement+job a billing cycle belongs to. The id
55
+ * carried here is the underlying `TalentEngagement.id`, NOT the
56
+ * `JobActivityItem.id` exposed by `engagements list`. Callers that
57
+ * need to cross-reference back to the engagements row should use
58
+ * `engagements list` and match on `engagementId`.
59
+ */
60
+ export interface TimesheetEngagementRef {
61
+ id: string;
62
+ job: {
63
+ id: string;
64
+ title: string | null;
65
+ client: {
66
+ id: string;
67
+ fullName: string | null;
68
+ } | null;
69
+ };
70
+ }
71
+ /**
72
+ * One row in the timesheet list — surfaced by `timesheet list`. Wire
73
+ * type for `hours` is a string (e.g., `"8.0"`) — preserved as-is so
74
+ * the CLI can render it verbatim without decimal-precision surprises.
75
+ *
76
+ * `timesheetSubmissionOpenDatetime` / `timesheetSubmissionDeadlineDatetime`
77
+ * carry the wire ISO-8601 datetime strings. The CLI may render only
78
+ * the date portion in pretty output.
79
+ */
80
+ export interface TimesheetListItem {
81
+ /** BillingCycle.id — the public timesheet id. */
82
+ id: string;
83
+ startDate: string;
84
+ endDate: string;
85
+ hours: string;
86
+ minimumCommitment: TimesheetMinimumCommitment | null;
87
+ timesheetOverdue: boolean;
88
+ timesheetSubmissionOpenDatetime: string | null;
89
+ timesheetSubmissionDeadlineDatetime: string | null;
90
+ timesheetSubmitted: boolean;
91
+ engagement: TimesheetEngagementRef;
92
+ }
93
+ /**
94
+ * One time-entry within a timesheet (per-day duration). `duration` is
95
+ * the canonical wire field — a **string-encoded decimal** in **minutes**
96
+ * (e.g., `"480.0"` for an 8-hour day, `"0.0"` for a zero-hour day).
97
+ * `note` may be empty/null; `isDayOff === true` rows represent a marked
98
+ * day off and typically carry `duration === "0.0"`.
99
+ *
100
+ * **Wire-shape history**: this field was declared as `duration: number`
101
+ * (presumed seconds) until 2026-05-14. The live mobile-gateway returns
102
+ * a string-encoded minutes value, empirically captured during the first
103
+ * end-to-end `SubmitTimesheet` mutation run. The previous declaration
104
+ * caused `ttctl timesheet show` to render an 8-hour day as `0.13h`
105
+ * (because `"480.0" / 3600 ≈ 0.133`). See
106
+ * `.tmp/timesheet-submit-e2e-20260514/{01-before-show,03-submit}.json`
107
+ * for the originating capture.
108
+ */
109
+ export interface TimesheetRecord {
110
+ date: string;
111
+ duration: string;
112
+ note: string | null;
113
+ isDayOff: boolean;
114
+ }
115
+ /**
116
+ * Detail-view shape for `timesheet show <id>`. Extends
117
+ * {@link TimesheetListItem} with the timesheet records, the comment,
118
+ * the rate-card snapshot, and the points-of-contact projection from
119
+ * the engagement.
120
+ *
121
+ * Field selection mirrors the captured `timesheetDetailsFields`
122
+ * fragment.
123
+ */
124
+ export interface TimesheetDetail extends TimesheetListItem {
125
+ timesheetUrl: string | null;
126
+ timesheetComment: string | null;
127
+ timesheetRecords: TimesheetRecord[];
128
+ actualAgreement: {
129
+ applicationRate: string | null;
130
+ talentHourlyRate: string | null;
131
+ marketplaceMargin: string | null;
132
+ } | null;
133
+ engagement: TimesheetEngagementRef & {
134
+ expectedHours: number | null;
135
+ };
136
+ }
137
+ /**
138
+ * Optional list filter.
139
+ */
140
+ export interface ListOptions {
141
+ /**
142
+ * When set, scope the listing to the given engagement (the
143
+ * `JobActivityItem.id` from `engagements list`, NOT the underlying
144
+ * `TalentEngagement.id`). Switches the wire query from
145
+ * `PendingTimesheets` (viewer-wide pending-only) to
146
+ * `Timesheets(jobActivityItemId)` (all cycles for that engagement,
147
+ * regardless of submission state).
148
+ */
149
+ engagement?: string;
150
+ }
151
+ /**
152
+ * Result of {@link resolveCurrentCycle}.
153
+ *
154
+ * - `kind: "found"` — exactly one cycle's submission window contains
155
+ * "today" AND it's not yet submitted. Submit by `cycle.id`.
156
+ * - `kind: "none"` — zero cycles match. Either the user is too early
157
+ * (before the next cycle's `submissionOpenDatetime`) or too late
158
+ * (after the deadline of every pending cycle).
159
+ * - `kind: "multiple"` — more than one cycle matches. The CLI prompts
160
+ * the user to disambiguate by explicit id.
161
+ */
162
+ export type CurrentCycleResolution = {
163
+ kind: "found";
164
+ cycle: TimesheetListItem;
165
+ } | {
166
+ kind: "none";
167
+ } | {
168
+ kind: "multiple";
169
+ candidates: TimesheetListItem[];
170
+ };
171
+ /**
172
+ * Per-mutation option object for the dry-run short-circuit. Mirrors
173
+ * `engagements.DryRunOptions` (issue #163) and the CLI-global
174
+ * `--dry-run` flag (#52). When `dryRun === true`, {@link submit}
175
+ * builds a {@link DryRunPreview} and returns `{ kind: "preview",
176
+ * preview }` WITHOUT invoking the gateway transport. Default `false`
177
+ * — the apply path runs and a `{ kind: "applied", result }` outcome
178
+ * is returned.
179
+ *
180
+ * **`submit` specifics**: the apply path takes `id: string`
181
+ * (BillingCycle.id) and submits unconditionally. The dry-run path
182
+ * builds the preview directly off the supplied id. When the CLI
183
+ * runner is in auto-resolve mode (no positional id, dry-run engaged),
184
+ * it stamps a literal placeholder string (e.g.
185
+ * `<auto-resolved-at-apply-time>`) into `id` so the preview's
186
+ * `variables.id` is well-named and clearly NOT a real cycle id; the
187
+ * apply path resolves the real id via
188
+ * {@link resolveCurrentCycle} before submitting.
189
+ */
190
+ export interface DryRunOptions {
191
+ dryRun?: boolean;
192
+ }
193
+ /**
194
+ * Apply-path outcome for {@link submit}. Wraps the
195
+ * post-submission {@link TimesheetDetail} in a discriminated union so
196
+ * callers can branch deterministically between the apply path
197
+ * (`kind: "applied"`) and the dry-run path
198
+ * (`kind: "preview"`, see {@link TimesheetDryRunPreviewOutcome}).
199
+ */
200
+ export interface TimesheetSubmitAppliedOutcome {
201
+ kind: "applied";
202
+ result: TimesheetDetail;
203
+ }
204
+ /**
205
+ * Dry-run outcome for {@link submit}. Carries the
206
+ * {@link DryRunPreview} (operation name, surface, transport,
207
+ * endpoint, variables payload, redacted bearer header) — emitted
208
+ * verbatim by the CLI's dry-run envelope (`emitDryRunSuccess`).
209
+ */
210
+ export interface TimesheetDryRunPreviewOutcome {
211
+ kind: "preview";
212
+ preview: DryRunPreview;
213
+ }
214
+ /**
215
+ * Discriminated-union return type for {@link submit}.
216
+ */
217
+ export type SubmitOutcome = TimesheetSubmitAppliedOutcome | TimesheetDryRunPreviewOutcome;
218
+ /**
219
+ * Optional inputs for {@link resolveCurrentCycle}.
220
+ */
221
+ export interface ResolveCurrentCycleOptions {
222
+ /**
223
+ * Same semantic as {@link ListOptions.engagement}: scope the
224
+ * resolve to one engagement. When set, the helper switches its
225
+ * underlying query from `PendingTimesheets` to
226
+ * `Timesheets(jobActivityItemId)` and filters to
227
+ * `timesheetSubmitted === false` client-side.
228
+ */
229
+ engagement?: string;
230
+ /**
231
+ * Override "now" for deterministic tests. Defaults to
232
+ * `new Date()` at call time.
233
+ */
234
+ now?: Date;
235
+ }
236
+ /**
237
+ * List the signed-in user's timesheet billing cycles.
238
+ *
239
+ * Default (no `engagement` option): uses `PendingTimesheets` to fetch
240
+ * viewer-wide cycles that still need submission. This is the
241
+ * "what needs my attention" view.
242
+ *
243
+ * With `engagement` option: uses `Timesheets($jobActivityItemId)` to
244
+ * fetch ALL cycles for that engagement (regardless of submission
245
+ * state). The argument is the public `JobActivityItem.id` exposed by
246
+ * `engagements list`.
247
+ *
248
+ * The returned array preserves server order; the CLI / MCP do not
249
+ * re-sort.
250
+ */
251
+ export declare function list(token: string, opts?: ListOptions): Promise<TimesheetListItem[]>;
252
+ /**
253
+ * Fetch a single timesheet's detail by `BillingCycle.id`.
254
+ *
255
+ * Uses the gateway's `node(id)` polymorphic root with the captured
256
+ * `timesheetDetailsFields` fragment. Throws
257
+ * `TimesheetError("NOT_FOUND")` when the id doesn't resolve (matches
258
+ * both the `Record not found` GraphQL error path AND the data-shape
259
+ * sentinel `data.node === null`).
260
+ *
261
+ * Throws `TimesheetError("UNKNOWN")` if the node resolves but isn't a
262
+ * `BillingCycle` — that would be a wire surprise (a different `node`
263
+ * type carrying our fragment) and warrants surfacing rather than
264
+ * silent type-coercion.
265
+ */
266
+ export declare function show(token: string, id: string): Promise<TimesheetDetail>;
267
+ /**
268
+ * Resolve the "current" pending timesheet — the billing cycle whose
269
+ * submission window contains `now` AND which is not yet submitted.
270
+ *
271
+ * - `kind: "found"`: exactly one cycle matches.
272
+ * - `kind: "none"`: zero cycles match (too early before next window,
273
+ * too late after every cycle's deadline, or no engagements have
274
+ * timesheets enabled).
275
+ * - `kind: "multiple"`: more than one cycle matches (parallel
276
+ * engagements with overlapping current windows).
277
+ *
278
+ * When `opts.engagement` is provided, the resolution scopes to that
279
+ * engagement (uses `Timesheets($jobActivityItemId)` + client-side
280
+ * filter to `timesheetSubmitted === false`). Otherwise uses
281
+ * `PendingTimesheets` (server-side filtered).
282
+ *
283
+ * The `now` option exists for deterministic testing; production code
284
+ * paths pass nothing and the helper uses `new Date()`.
285
+ */
286
+ export declare function resolveCurrentCycle(token: string, opts?: ResolveCurrentCycleOptions): Promise<CurrentCycleResolution>;
287
+ /**
288
+ * Submit a timesheet for billing.
289
+ *
290
+ * **Destructive**: the submission is one-way at the wire level — once
291
+ * submitted, the timesheet enters Toptal's billing pipeline. Callers
292
+ * (CLI / MCP) are responsible for end-user confirmation.
293
+ *
294
+ * `id` is the BillingCycle.id from `list()` / `show()`. Returns the
295
+ * post-submission detail payload (with `timesheetSubmitted: true`)
296
+ * wrapped in `{ kind: "applied", result }` on the apply path.
297
+ *
298
+ * Dry-run path (`options.dryRun === true`): builds a
299
+ * {@link DryRunPreview} of the mutation WITHOUT invoking the
300
+ * gateway transport. Returns `{ kind: "preview", preview }`. The
301
+ * CLI's `--dry-run` flag flows through here so the destructive
302
+ * mutation is never sent in preview mode. See {@link DryRunOptions}
303
+ * for the placeholder-id semantics when the CLI is in auto-resolve
304
+ * mode.
305
+ *
306
+ * Throws (apply path only — dry-run never throws domain errors):
307
+ * - `TimesheetError("NOT_FOUND")` when the id doesn't resolve to a
308
+ * billing cycle the viewer can submit AND the server is willing to
309
+ * communicate that as a structured error (Relay-style global-id
310
+ * decode failure, matched via {@link NOT_FOUND_MESSAGE_PATTERN}).
311
+ * - `TimesheetError("GRAPHQL_ERROR")` when the server returns a
312
+ * top-level GraphQL error other than the Relay decode pattern.
313
+ * Empirically (E2E 2026-05-12), Toptal's `SubmitTimesheet` returns
314
+ * `"500: Internal Server Error"` for syntactically valid but
315
+ * non-existent BillingCycle ids — the wire does not pre-validate the
316
+ * id, so the 500 surfaces verbatim. The CLI presents it as
317
+ * `GRAPHQL_ERROR` (not `NOT_FOUND`) to avoid misleading the caller
318
+ * into thinking the id was definitively absent vs. the server
319
+ * genuinely failed.
320
+ * - `TimesheetError("MUTATION_ERROR")` when the server reports
321
+ * `success: false` on `MutationResult` (commonly: missing required
322
+ * hours, already submitted, deadline passed). The message carries
323
+ * the server-side error code+key+message tuples.
324
+ */
325
+ export declare function submit(token: string, id: string, options?: DryRunOptions): Promise<SubmitOutcome>;
326
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/timesheet/index.ts"],"names":[],"mappings":"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"}