@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.
- package/README.md +49 -9
- package/dist/__generated__/gateway.d.ts +4546 -0
- package/dist/__generated__/gateway.d.ts.map +1 -0
- package/dist/__generated__/gateway.js +9 -0
- package/dist/__generated__/gateway.js.map +1 -0
- package/dist/__generated__/talent-profile-zod-schemas.d.ts +1187 -0
- package/dist/__generated__/talent-profile-zod-schemas.d.ts.map +1 -0
- package/dist/__generated__/talent-profile-zod-schemas.js +1136 -0
- package/dist/__generated__/talent-profile-zod-schemas.js.map +1 -0
- package/dist/__generated__/talent-profile.d.ts +1397 -0
- package/dist/__generated__/talent-profile.d.ts.map +1 -0
- package/dist/__generated__/talent-profile.js +9 -0
- package/dist/__generated__/talent-profile.js.map +1 -0
- package/dist/__generated__/zod-schemas.d.ts +2895 -0
- package/dist/__generated__/zod-schemas.d.ts.map +1 -0
- package/dist/__generated__/zod-schemas.js +3121 -0
- package/dist/__generated__/zod-schemas.js.map +1 -0
- package/dist/__tests__/fixtures/profile/builders.d.ts +74 -0
- package/dist/__tests__/fixtures/profile/builders.d.ts.map +1 -0
- package/dist/__tests__/fixtures/profile/builders.js +196 -0
- package/dist/__tests__/fixtures/profile/builders.js.map +1 -0
- package/dist/__tests__/fixtures/profile/data.d.ts +39 -0
- package/dist/__tests__/fixtures/profile/data.d.ts.map +1 -0
- package/dist/__tests__/fixtures/profile/data.js +230 -0
- package/dist/__tests__/fixtures/profile/data.js.map +1 -0
- package/dist/__tests__/fixtures/profile/index.d.ts +9 -0
- package/dist/__tests__/fixtures/profile/index.d.ts.map +1 -0
- package/dist/__tests__/fixtures/profile/index.js +10 -0
- package/dist/__tests__/fixtures/profile/index.js.map +1 -0
- package/dist/__tests__/fixtures/profile/types.d.ts +53 -0
- package/dist/__tests__/fixtures/profile/types.d.ts.map +1 -0
- package/dist/__tests__/fixtures/profile/types.js +4 -0
- package/dist/__tests__/fixtures/profile/types.js.map +1 -0
- package/dist/auth/errors.d.ts +82 -0
- package/dist/auth/errors.d.ts.map +1 -0
- package/dist/auth/errors.js +68 -0
- package/dist/auth/errors.js.map +1 -0
- package/dist/auth.d.ts +192 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +294 -0
- package/dist/auth.js.map +1 -0
- package/dist/config.d.ts +212 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +349 -0
- package/dist/config.js.map +1 -0
- package/dist/configLock.d.ts +50 -0
- package/dist/configLock.d.ts.map +1 -0
- package/dist/configLock.js +88 -0
- package/dist/configLock.js.map +1 -0
- package/dist/configWriter.d.ts +97 -0
- package/dist/configWriter.d.ts.map +1 -0
- package/dist/configWriter.js +687 -0
- package/dist/configWriter.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/kill-switch.d.ts +161 -0
- package/dist/kill-switch.d.ts.map +1 -0
- package/dist/kill-switch.js +235 -0
- package/dist/kill-switch.js.map +1 -0
- package/dist/lib/date.d.ts +58 -0
- package/dist/lib/date.d.ts.map +1 -0
- package/dist/lib/date.js +104 -0
- package/dist/lib/date.js.map +1 -0
- package/dist/lib/diagnostic-log.d.ts +159 -0
- package/dist/lib/diagnostic-log.d.ts.map +1 -0
- package/dist/lib/diagnostic-log.js +186 -0
- package/dist/lib/diagnostic-log.js.map +1 -0
- package/dist/lib/package-version.d.ts +19 -0
- package/dist/lib/package-version.d.ts.map +1 -0
- package/dist/lib/package-version.js +38 -0
- package/dist/lib/package-version.js.map +1 -0
- package/dist/lib/redact.d.ts +153 -0
- package/dist/lib/redact.d.ts.map +1 -0
- package/dist/lib/redact.js +207 -0
- package/dist/lib/redact.js.map +1 -0
- package/dist/lib/text.d.ts +14 -0
- package/dist/lib/text.d.ts.map +1 -0
- package/dist/lib/text.js +21 -0
- package/dist/lib/text.js.map +1 -0
- package/dist/lib/wire-shape.d.ts +131 -0
- package/dist/lib/wire-shape.d.ts.map +1 -0
- package/dist/lib/wire-shape.js +376 -0
- package/dist/lib/wire-shape.js.map +1 -0
- package/dist/onepassword.d.ts +29 -0
- package/dist/onepassword.d.ts.map +1 -0
- package/dist/onepassword.js +112 -0
- package/dist/onepassword.js.map +1 -0
- package/dist/services/_shared/transport.d.ts +148 -0
- package/dist/services/_shared/transport.d.ts.map +1 -0
- package/dist/services/_shared/transport.js +102 -0
- package/dist/services/_shared/transport.js.map +1 -0
- package/dist/services/applications/index.d.ts +210 -0
- package/dist/services/applications/index.d.ts.map +1 -0
- package/dist/services/applications/index.js +240 -0
- package/dist/services/applications/index.js.map +1 -0
- package/dist/services/availability/index.d.ts +254 -0
- package/dist/services/availability/index.d.ts.map +1 -0
- package/dist/services/availability/index.js +310 -0
- package/dist/services/availability/index.js.map +1 -0
- package/dist/services/contracts/index.d.ts +132 -0
- package/dist/services/contracts/index.d.ts.map +1 -0
- package/dist/services/contracts/index.js +211 -0
- package/dist/services/contracts/index.js.map +1 -0
- package/dist/services/engagements/index.d.ts +504 -0
- package/dist/services/engagements/index.d.ts.map +1 -0
- package/dist/services/engagements/index.js +613 -0
- package/dist/services/engagements/index.js.map +1 -0
- package/dist/services/jobs/index.d.ts +490 -0
- package/dist/services/jobs/index.d.ts.map +1 -0
- package/dist/services/jobs/index.js +753 -0
- package/dist/services/jobs/index.js.map +1 -0
- package/dist/services/payments/index.d.ts +415 -0
- package/dist/services/payments/index.d.ts.map +1 -0
- package/dist/services/payments/index.js +636 -0
- package/dist/services/payments/index.js.map +1 -0
- package/dist/services/profile/__tests__/fixtures.d.ts +214 -0
- package/dist/services/profile/__tests__/fixtures.d.ts.map +1 -0
- package/dist/services/profile/__tests__/fixtures.js +176 -0
- package/dist/services/profile/__tests__/fixtures.js.map +1 -0
- package/dist/services/profile/basic/index.d.ts +390 -0
- package/dist/services/profile/basic/index.d.ts.map +1 -0
- package/dist/services/profile/basic/index.js +1007 -0
- package/dist/services/profile/basic/index.js.map +1 -0
- package/dist/services/profile/certifications/index.d.ts +74 -0
- package/dist/services/profile/certifications/index.d.ts.map +1 -0
- package/dist/services/profile/certifications/index.js +169 -0
- package/dist/services/profile/certifications/index.js.map +1 -0
- package/dist/services/profile/education/index.d.ts +73 -0
- package/dist/services/profile/education/index.d.ts.map +1 -0
- package/dist/services/profile/education/index.js +168 -0
- package/dist/services/profile/education/index.js.map +1 -0
- package/dist/services/profile/employment/index.d.ts +111 -0
- package/dist/services/profile/employment/index.d.ts.map +1 -0
- package/dist/services/profile/employment/index.js +202 -0
- package/dist/services/profile/employment/index.js.map +1 -0
- package/dist/services/profile/external/index.d.ts +219 -0
- package/dist/services/profile/external/index.d.ts.map +1 -0
- package/dist/services/profile/external/index.js +560 -0
- package/dist/services/profile/external/index.js.map +1 -0
- package/dist/services/profile/index.d.ts +24 -0
- package/dist/services/profile/index.d.ts.map +1 -0
- package/dist/services/profile/index.js +26 -0
- package/dist/services/profile/index.js.map +1 -0
- package/dist/services/profile/industries/index.d.ts +130 -0
- package/dist/services/profile/industries/index.d.ts.map +1 -0
- package/dist/services/profile/industries/index.js +292 -0
- package/dist/services/profile/industries/index.js.map +1 -0
- package/dist/services/profile/portfolio/index.d.ts +352 -0
- package/dist/services/profile/portfolio/index.d.ts.map +1 -0
- package/dist/services/profile/portfolio/index.js +833 -0
- package/dist/services/profile/portfolio/index.js.map +1 -0
- package/dist/services/profile/resume/index.d.ts +60 -0
- package/dist/services/profile/resume/index.d.ts.map +1 -0
- package/dist/services/profile/resume/index.js +212 -0
- package/dist/services/profile/resume/index.js.map +1 -0
- package/dist/services/profile/reviews/index.d.ts +137 -0
- package/dist/services/profile/reviews/index.d.ts.map +1 -0
- package/dist/services/profile/reviews/index.js +431 -0
- package/dist/services/profile/reviews/index.js.map +1 -0
- package/dist/services/profile/shared.d.ts +127 -0
- package/dist/services/profile/shared.d.ts.map +1 -0
- package/dist/services/profile/shared.js +155 -0
- package/dist/services/profile/shared.js.map +1 -0
- package/dist/services/profile/skills/index.d.ts +212 -0
- package/dist/services/profile/skills/index.d.ts.map +1 -0
- package/dist/services/profile/skills/index.js +461 -0
- package/dist/services/profile/skills/index.js.map +1 -0
- package/dist/services/profile/visas/index.d.ts +74 -0
- package/dist/services/profile/visas/index.d.ts.map +1 -0
- package/dist/services/profile/visas/index.js +306 -0
- package/dist/services/profile/visas/index.js.map +1 -0
- package/dist/services/timesheet/index.d.ts +326 -0
- package/dist/services/timesheet/index.d.ts.map +1 -0
- package/dist/services/timesheet/index.js +324 -0
- package/dist/services/timesheet/index.js.map +1 -0
- package/dist/services/translations.d.ts +79 -0
- package/dist/services/translations.d.ts.map +1 -0
- package/dist/services/translations.js +136 -0
- package/dist/services/translations.js.map +1 -0
- package/dist/transport-resilience.d.ts +136 -0
- package/dist/transport-resilience.d.ts.map +1 -0
- package/dist/transport-resilience.js +247 -0
- package/dist/transport-resilience.js.map +1 -0
- package/dist/transport.d.ts +408 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +691 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +41 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -0
- package/package.json +40 -12
- package/index.js +0 -7
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (C) 2026 Oleksii PELYKH
|
|
3
|
+
import { AuthRevokedError, TtctlError } from "../../auth/errors.js";
|
|
4
|
+
import { buildWireShapeError } from "../../lib/wire-shape.js";
|
|
5
|
+
import { impersonatedTransport } from "../../transport.js";
|
|
6
|
+
import { show as basicShow, ProfileError } from "./basic/index.js";
|
|
7
|
+
/**
|
|
8
|
+
* Returns `true` when `extensions.code` on a GraphQL error indicates the
|
|
9
|
+
* session token has been revoked or expired and the user must re-run
|
|
10
|
+
* `ttctl auth signin`.
|
|
11
|
+
*
|
|
12
|
+
* Recognized stable codes (accumulated across surfaces and history):
|
|
13
|
+
*
|
|
14
|
+
* - `'UNAUTHENTICATED'` — talent-profile (Cloudflare-protected, web-portal API)
|
|
15
|
+
* - `'AUTHENTICATION_REQUIRED'` — defensive carryover from #77 (provenance unverified;
|
|
16
|
+
* see "Empirical history" below)
|
|
17
|
+
* - `'UNAUTHORIZED'` — mobile-gateway (federated `talent_schema` subgraph,
|
|
18
|
+
* empirically observed 2026-05-07 for invalid bearer
|
|
19
|
+
* tokens; see `research/notes/14-auth-error-extensions-code.md`)
|
|
20
|
+
*
|
|
21
|
+
* All three collapse to `AuthRevokedError` so the CLI / MCP surfaces apply a
|
|
22
|
+
* uniform "Run `ttctl auth signin`" recovery hint regardless of which surface
|
|
23
|
+
* raised the failure.
|
|
24
|
+
*
|
|
25
|
+
* **Empirical history** (issue #89): the original predicate (#77) recognized
|
|
26
|
+
* only the first two codes. Live mobile-gateway capture for an invalid bearer
|
|
27
|
+
* token returned HTTP 200 with `errors[0].extensions.code = 'UNAUTHORIZED'`
|
|
28
|
+
* (NOT `'AUTHENTICATION_REQUIRED'` as documentation suggested). All token-
|
|
29
|
+
* shape variants (corrupted, malformed, no-auth, plausible-but-invalid) flow
|
|
30
|
+
* through the same code path with identical responses, so this single code
|
|
31
|
+
* covers every "gateway can't resolve the token to an account" case.
|
|
32
|
+
*
|
|
33
|
+
* Future drift: a server-side rename adds another `||` clause here. New codes
|
|
34
|
+
* MUST be empirically captured before being added — see the research note for
|
|
35
|
+
* the capture procedure.
|
|
36
|
+
*/
|
|
37
|
+
export function isAuthRevokedExtensionCode(code) {
|
|
38
|
+
return code === "UNAUTHENTICATED" || code === "AUTHENTICATION_REQUIRED" || code === "UNAUTHORIZED";
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Resolve the signed-in user's `profileId` for use in mutation inputs that
|
|
42
|
+
* follow Pattern 2 (`{ profileId, <entity>: <Entity>Input }`) — see
|
|
43
|
+
* `research/notes/10-mutation-input-patterns.md`. Lazily fetches the
|
|
44
|
+
* profile via `profile.basic.show()` against the mobile-gateway surface,
|
|
45
|
+
* then extracts `viewerRole.profileId`.
|
|
46
|
+
*
|
|
47
|
+
* Errors propagate verbatim — a write attempt that can't read its own
|
|
48
|
+
* profile is unrecoverable, and surfacing the read-side error gives the
|
|
49
|
+
* user the same actionable message as `ttctl profile show`.
|
|
50
|
+
*/
|
|
51
|
+
export async function extractProfileId(token) {
|
|
52
|
+
const profile = await basicShow(token);
|
|
53
|
+
const profileId = profile.viewer?.viewerRole.profileId;
|
|
54
|
+
if (profileId === undefined) {
|
|
55
|
+
throw new ProfileError("NO_VIEWER", "Cannot resolve profileId: viewer or profile id missing from the session response.");
|
|
56
|
+
}
|
|
57
|
+
return profileId;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Fire a GraphQL request against the talent-profile surface and turn
|
|
61
|
+
* transport-level outcomes into typed errors consistent across the four
|
|
62
|
+
* sub-domains landing in #74.
|
|
63
|
+
*
|
|
64
|
+
* Error mapping:
|
|
65
|
+
* - Underlying typed errors (`TtctlError` subclasses — `Cf403Error`,
|
|
66
|
+
* `AuthRevokedError`, …) propagate as-is so the CLI / MCP surfaces can
|
|
67
|
+
* render their `recovery` hints.
|
|
68
|
+
* - Other transport throws become `ProfileError("NETWORK_ERROR")` with
|
|
69
|
+
* the underlying message tagged by `verb` (e.g. "education list").
|
|
70
|
+
* - HTTP 401 becomes `AuthRevokedError`.
|
|
71
|
+
* - Non-2xx becomes `ProfileError("UNKNOWN")`.
|
|
72
|
+
*
|
|
73
|
+
* Top-level GraphQL `errors` are NOT inspected here — callers handle
|
|
74
|
+
* those via {@link ensureNoTopLevelErrors} once they've narrowed the body
|
|
75
|
+
* to their expected shape.
|
|
76
|
+
*
|
|
77
|
+
* Optional `schema` parameter (Z-3 / #286): when provided, the
|
|
78
|
+
* response `body.data` is validated against the schema as a SIDE
|
|
79
|
+
* EFFECT before the helper returns; on `ZodError` the call throws
|
|
80
|
+
* `ProfileError("WIRE_SHAPE_ERROR")` with the original ZodError
|
|
81
|
+
* chained via `cause` and a field-level diff in the message per
|
|
82
|
+
* `docs/wire-validation-error-format.md`. The return shape is
|
|
83
|
+
* unchanged (`TransportResponse`) — callers still narrow `res.body`
|
|
84
|
+
* to their per-operation type. Schema validation is a guard rail; it
|
|
85
|
+
* does NOT mutate the response. When omitted, the existing pass-
|
|
86
|
+
* through behavior is preserved. No production op wires `schema` in
|
|
87
|
+
* Wave 3; Z-4 (#288) ships the first beachhead.
|
|
88
|
+
*/
|
|
89
|
+
export async function callTalentProfile(token, operationName, query, variables, verb, schema) {
|
|
90
|
+
let res;
|
|
91
|
+
try {
|
|
92
|
+
res = await impersonatedTransport({
|
|
93
|
+
surface: "talent-profile",
|
|
94
|
+
authToken: token,
|
|
95
|
+
body: { operationName, query, variables },
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
if (err instanceof TtctlError)
|
|
100
|
+
throw err;
|
|
101
|
+
throw new ProfileError("NETWORK_ERROR", `${verb} request failed: ${err.message}`, {
|
|
102
|
+
cause: err,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (res.status === 401)
|
|
106
|
+
throw new AuthRevokedError("Session is invalid or expired.");
|
|
107
|
+
if (res.status < 200 || res.status >= 300) {
|
|
108
|
+
throw new ProfileError("UNKNOWN", `${verb} returned HTTP ${res.status.toString()}`);
|
|
109
|
+
}
|
|
110
|
+
if (schema !== undefined) {
|
|
111
|
+
const body = res.body;
|
|
112
|
+
if (body?.data !== undefined && body.data !== null) {
|
|
113
|
+
const parsed = schema.safeParse(body.data);
|
|
114
|
+
if (!parsed.success) {
|
|
115
|
+
const payload = buildWireShapeError(operationName, parsed.error, body.data);
|
|
116
|
+
throw new ProfileError("WIRE_SHAPE_ERROR", payload.message, { cause: parsed.error });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return res;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Throw a typed error when the GraphQL response carries a non-empty
|
|
124
|
+
* top-level `errors` array. Auth-revoked codes (per
|
|
125
|
+
* {@link isAuthRevokedExtensionCode}) become `AuthRevokedError`; anything
|
|
126
|
+
* else becomes `ProfileError("GRAPHQL_ERROR")` tagged by `verb`.
|
|
127
|
+
*/
|
|
128
|
+
export function ensureNoTopLevelErrors(body, verb) {
|
|
129
|
+
if (body && Array.isArray(body.errors) && body.errors.length > 0) {
|
|
130
|
+
const first = body.errors[0];
|
|
131
|
+
if (isAuthRevokedExtensionCode(first?.extensions?.code)) {
|
|
132
|
+
throw new AuthRevokedError("Session is invalid or expired.");
|
|
133
|
+
}
|
|
134
|
+
throw new ProfileError("GRAPHQL_ERROR", `${verb} failed: ${first?.message ?? "GraphQL error"}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Inspect a mutation payload for `success === false` or a non-empty
|
|
139
|
+
* `errors` array and convert either into `ProfileError("USER_ERROR")`. The
|
|
140
|
+
* common shape across the four sub-domains' mutation payloads is
|
|
141
|
+
* `{ success?, notice?, errors?: UserError[] }`; this helper handles
|
|
142
|
+
* exactly that subset and lets each sub-domain narrow the rest of the
|
|
143
|
+
* payload itself.
|
|
144
|
+
*/
|
|
145
|
+
export function applyUserErrorsAndSuccess(payload, verb) {
|
|
146
|
+
if (Array.isArray(payload.errors) && payload.errors.length > 0) {
|
|
147
|
+
const first = payload.errors[0];
|
|
148
|
+
const fieldHint = first?.key ? ` (${first.key})` : "";
|
|
149
|
+
throw new ProfileError("USER_ERROR", `${verb} rejected${fieldHint}: ${first?.message ?? "unknown error"}`);
|
|
150
|
+
}
|
|
151
|
+
if (payload.success === false) {
|
|
152
|
+
throw new ProfileError("USER_ERROR", `${verb} reported success=false${payload.notice ? `: ${payload.notice}` : ""}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/services/profile/shared.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills-domain error codes. Mirrors `profile.basic.ProfileErrorCode`'s
|
|
3
|
+
* shape (NO_VIEWER, GRAPHQL_ERROR, NETWORK_ERROR, USER_ERROR, VALIDATION_ERROR,
|
|
4
|
+
* UNKNOWN) — the duplication is intentional so each sub-domain can carry
|
|
5
|
+
* its own typed error class without callers having to import a shared
|
|
6
|
+
* cross-domain enum. Auth-revoked failures throw `AuthRevokedError`
|
|
7
|
+
* (cross-cutting `TtctlError` subclass per issue #77), not a code on this
|
|
8
|
+
* enum.
|
|
9
|
+
*/
|
|
10
|
+
export type SkillsErrorCode = "NO_VIEWER" | "GRAPHQL_ERROR" | "NETWORK_ERROR" | "USER_ERROR" | "VALIDATION_ERROR" | "PARTIAL_FAILURE" | "WIRE_SHAPE_ERROR" | "UNKNOWN";
|
|
11
|
+
export declare class SkillsError extends Error {
|
|
12
|
+
readonly code: SkillsErrorCode;
|
|
13
|
+
readonly name = "SkillsError";
|
|
14
|
+
constructor(code: SkillsErrorCode, message: string, options?: {
|
|
15
|
+
cause?: unknown;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Proficiency rating on a `ProfileSkillSet`. Confirmed enum values from
|
|
20
|
+
* `research/notes/10-mutation-input-patterns.md`. The wire-format `NOVICE`
|
|
21
|
+
* variant is documented as "possibly" present but not exercised by the
|
|
22
|
+
* write path — accept-on-read, validate-on-write.
|
|
23
|
+
*/
|
|
24
|
+
export type ProficiencyRating = "COMPETENT" | "STRONG" | "EXPERT" | "NOVICE";
|
|
25
|
+
/**
|
|
26
|
+
* Visibility on a `ProfileSkillSet`. Boolean wire-format value drives the
|
|
27
|
+
* `--public` / `--private` CLI flag pair (CLI translates either flag to
|
|
28
|
+
* the same boolean before calling `set()`).
|
|
29
|
+
*/
|
|
30
|
+
export type SkillVisibility = boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Full skill set as exposed to consumers. Mirrors the GraphQL
|
|
33
|
+
* `ProfileSkillSet` selection set we ask for, normalised so optional
|
|
34
|
+
* fields surface as `null` rather than `undefined` (matches the
|
|
35
|
+
* `profile.basic` convention).
|
|
36
|
+
*/
|
|
37
|
+
export interface ProfileSkillSet {
|
|
38
|
+
id: string;
|
|
39
|
+
experience: number | null;
|
|
40
|
+
rating: ProficiencyRating | null;
|
|
41
|
+
public: boolean;
|
|
42
|
+
position: number | null;
|
|
43
|
+
skill: {
|
|
44
|
+
id: string;
|
|
45
|
+
name: string;
|
|
46
|
+
};
|
|
47
|
+
connectionsCount: number;
|
|
48
|
+
}
|
|
49
|
+
/** Skill suggestion returned by `autocomplete`. */
|
|
50
|
+
export interface SkillSuggestion {
|
|
51
|
+
id: string;
|
|
52
|
+
name: string;
|
|
53
|
+
}
|
|
54
|
+
/** Skill-readiness flags. Each boolean reflects a sub-criterion. */
|
|
55
|
+
export interface SkillsReadiness {
|
|
56
|
+
isExpertProficiencyCountSatisfied: boolean;
|
|
57
|
+
isHighlightedItemsCountAndExperienceSatisfied: boolean;
|
|
58
|
+
isItemsCountSatisfied: boolean;
|
|
59
|
+
isProficiencyNotSetCountSatisfied: boolean;
|
|
60
|
+
isProgrammingLanguageSatisfied: boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Multi-flag input for {@link set}. AT LEAST one of `rating` /
|
|
64
|
+
* `experience` / `public` must be supplied; an empty object is rejected
|
|
65
|
+
* with `VALIDATION_ERROR` (callers can detect the "user provided no
|
|
66
|
+
* flags" case at the CLI / MCP layer too — the duplicate gate keeps the
|
|
67
|
+
* service self-defending).
|
|
68
|
+
*
|
|
69
|
+
* `experience` is the talent's total years/months on this skill,
|
|
70
|
+
* expressed as an integer count of months on the wire; the CLI accepts a
|
|
71
|
+
* duration string (`"5y"`, `"60"`) and converts before calling.
|
|
72
|
+
*/
|
|
73
|
+
export interface SkillUpdate {
|
|
74
|
+
rating?: ProficiencyRating;
|
|
75
|
+
experience?: number;
|
|
76
|
+
public?: boolean;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Result of {@link set}. Records the post-update server-confirmed state
|
|
80
|
+
* for each field that was touched, plus the collected `notice` strings
|
|
81
|
+
* (one per fired mutation). Callers display the notices verbatim — the
|
|
82
|
+
* server uses them for soft signals like "Profile review may be required".
|
|
83
|
+
*/
|
|
84
|
+
export interface UpdateSkillResult {
|
|
85
|
+
id: string;
|
|
86
|
+
rating: ProficiencyRating | null;
|
|
87
|
+
experience: number | null;
|
|
88
|
+
public: boolean | null;
|
|
89
|
+
notices: string[];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Add a skill to the signed-in user's profile. Identifies the skill by
|
|
93
|
+
* its catalog name (e.g. `"TypeScript"`); the server resolves the name to
|
|
94
|
+
* a `Skill` id under the hood and returns the newly-attached
|
|
95
|
+
* `ProfileSkillSet` with default `rating`/`experience`/`public` values.
|
|
96
|
+
*
|
|
97
|
+
* Use {@link autocomplete} first if the caller needs to disambiguate
|
|
98
|
+
* between candidate skills (e.g., "Postgres" vs "PostgreSQL"). Once the
|
|
99
|
+
* skill is added, configure proficiency via {@link set}.
|
|
100
|
+
*
|
|
101
|
+
* Errors:
|
|
102
|
+
* - `SkillsError` `VALIDATION_ERROR` when `name` is empty or whitespace-only.
|
|
103
|
+
* - `SkillsError` `USER_ERROR` when the server reports a domain failure
|
|
104
|
+
* (e.g., skill already on profile, name not in catalog).
|
|
105
|
+
* - `AuthRevokedError`, `Cf403Error`, plus the standard
|
|
106
|
+
* `GRAPHQL_ERROR`/`NETWORK_ERROR`/`UNKNOWN` codes from the shared
|
|
107
|
+
* transport-error path.
|
|
108
|
+
*/
|
|
109
|
+
export declare function add(token: string, name: string): Promise<ProfileSkillSet>;
|
|
110
|
+
/**
|
|
111
|
+
* Remove a skill from the signed-in user's profile by its ProfileSkillSet
|
|
112
|
+
* id (NOT the Skill catalog id). Removal cascades to any connections the
|
|
113
|
+
* skill set held to portfolio items, education, employment, or
|
|
114
|
+
* certifications — the server cleans those up server-side, no per-edge
|
|
115
|
+
* call required from the client.
|
|
116
|
+
*
|
|
117
|
+
* Errors:
|
|
118
|
+
* - `SkillsError` `VALIDATION_ERROR` when `id` is empty.
|
|
119
|
+
* - `SkillsError` `USER_ERROR` when the id doesn't match a skill set on
|
|
120
|
+
* the user's profile (caller used a stale id).
|
|
121
|
+
* - Standard transport-error path (auth-revoked / Cf403 / GraphQL /
|
|
122
|
+
* network / unknown).
|
|
123
|
+
*/
|
|
124
|
+
export declare function rm(token: string, id: string): Promise<void>;
|
|
125
|
+
/**
|
|
126
|
+
* Update one or more fields on an existing skill set. Multi-flag atomic
|
|
127
|
+
* semantics: each provided field fires its own GraphQL mutation
|
|
128
|
+
* (`UPDATE_PROFILE_SKILL_SET_RATING` / `_EXPERIENCE` / `_PUBLICITY`)
|
|
129
|
+
* sequentially. **Partial-failure behavior**: if mutation N succeeds but
|
|
130
|
+
* mutation N+1 fails, the prior changes are NOT rolled back — the server
|
|
131
|
+
* lacks a multi-field atomic path, so the client serialises the updates
|
|
132
|
+
* and reports a `PARTIAL_FAILURE` error carrying which fields landed and
|
|
133
|
+
* which didn't. Callers re-issue only the missing fields after handling
|
|
134
|
+
* the failure.
|
|
135
|
+
*
|
|
136
|
+
* The mutation order is `rating → experience → public`, deterministic so
|
|
137
|
+
* tests can predict the call sequence.
|
|
138
|
+
*
|
|
139
|
+
* Errors:
|
|
140
|
+
* - `SkillsError` `VALIDATION_ERROR` when `id` is empty OR no fields
|
|
141
|
+
* are supplied.
|
|
142
|
+
* - `SkillsError` `PARTIAL_FAILURE` when the second or third mutation
|
|
143
|
+
* fails after at least one earlier mutation succeeded. The error
|
|
144
|
+
* message names the failing field and the underlying message; the
|
|
145
|
+
* error's `cause` carries the per-field failure for callers that want
|
|
146
|
+
* structured access. The successful fields are reflected in the
|
|
147
|
+
* `result` snapshot included on the error.
|
|
148
|
+
* - `SkillsError` `USER_ERROR` when the FIRST mutation fails — caller
|
|
149
|
+
* sees the same shape they'd see from a single-flag invocation.
|
|
150
|
+
* - Standard transport-error path.
|
|
151
|
+
*/
|
|
152
|
+
export declare function set(token: string, id: string, fields: SkillUpdate): Promise<UpdateSkillResult>;
|
|
153
|
+
/**
|
|
154
|
+
* Fetch a single skill set by its id, including its connection edges to
|
|
155
|
+
* portfolio items / employment / education / certifications. Useful when
|
|
156
|
+
* the caller already has the id (from `list()` or a recent mutation) and
|
|
157
|
+
* needs to display the full detail without paging.
|
|
158
|
+
*
|
|
159
|
+
* Errors:
|
|
160
|
+
* - `SkillsError` `VALIDATION_ERROR` when `id` is empty.
|
|
161
|
+
* - `SkillsError` `USER_ERROR` when the id doesn't resolve to a
|
|
162
|
+
* `ProfileSkillSet` (server returns `node: null`).
|
|
163
|
+
* - Standard transport-error path.
|
|
164
|
+
*/
|
|
165
|
+
export declare function show(token: string, id: string): Promise<ProfileSkillSet>;
|
|
166
|
+
/**
|
|
167
|
+
* List every skill set on the signed-in user's profile, with each entry
|
|
168
|
+
* carrying its connection-count summary (the digits the portal shows next
|
|
169
|
+
* to "linked to N items"). Order matches the server's `position` field;
|
|
170
|
+
* callers that need a specific sort apply it after fetching.
|
|
171
|
+
*
|
|
172
|
+
* The talent-profile surface keys this query on `profileId` rather than
|
|
173
|
+
* resolving it server-side from the auth token, so the caller passes the
|
|
174
|
+
* `profileId` it has on hand (typically obtained from `profile.basic.show`
|
|
175
|
+
* or cached for the session).
|
|
176
|
+
*
|
|
177
|
+
* Errors:
|
|
178
|
+
* - `SkillsError` `VALIDATION_ERROR` when `profileId` is empty.
|
|
179
|
+
* - `SkillsError` `USER_ERROR` when the profile id doesn't resolve.
|
|
180
|
+
* - Standard transport-error path.
|
|
181
|
+
*/
|
|
182
|
+
export declare function list(token: string, profileId: string): Promise<ProfileSkillSet[]>;
|
|
183
|
+
/**
|
|
184
|
+
* Search the global skill catalog for entries matching `search`,
|
|
185
|
+
* suitable for populating an autocomplete dropdown. The server scopes
|
|
186
|
+
* results to skills the talent's vertical permits and excludes any ids
|
|
187
|
+
* passed in `withoutIds` (typically the talent's existing skill-set
|
|
188
|
+
* skill ids, so the dropdown doesn't suggest skills they already have).
|
|
189
|
+
*
|
|
190
|
+
* `limit` defaults to 10 (matches the portal's default page size).
|
|
191
|
+
*
|
|
192
|
+
* Errors:
|
|
193
|
+
* - `SkillsError` `VALIDATION_ERROR` when `profileId` or `search` is empty.
|
|
194
|
+
* - Standard transport-error path.
|
|
195
|
+
*/
|
|
196
|
+
export declare function autocomplete(token: string, profileId: string, search: string, options?: {
|
|
197
|
+
limit?: number;
|
|
198
|
+
withoutIds?: string[];
|
|
199
|
+
}): Promise<SkillSuggestion[]>;
|
|
200
|
+
/**
|
|
201
|
+
* Fetch the skill-readiness snapshot for the user's profile — the same
|
|
202
|
+
* heuristics the portal uses to gate the "ready to apply" state on the
|
|
203
|
+
* skills section. Useful for surfacing a checklist of remaining tasks
|
|
204
|
+
* via CLI or MCP.
|
|
205
|
+
*
|
|
206
|
+
* Errors:
|
|
207
|
+
* - `SkillsError` `VALIDATION_ERROR` when `profileId` is empty.
|
|
208
|
+
* - `SkillsError` `USER_ERROR` when the profile id doesn't resolve.
|
|
209
|
+
* - Standard transport-error path.
|
|
210
|
+
*/
|
|
211
|
+
export declare function readiness(token: string, profileId: string): Promise<SkillsReadiness>;
|
|
212
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/services/profile/skills/index.ts"],"names":[],"mappings":"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"}
|