@cortexkit/claustrum-client 0.4.0 → 0.5.0

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 CHANGED
@@ -16,6 +16,11 @@ credential caching, refresh, account scheduling, and retry policy.
16
16
  by design (the vault cannot enumerate which). Treat it as gone either way: re-run
17
17
  `ck auth migrate-opencode` to mint a fresh handle; the prior record, if any, is kept.
18
18
  • `auth_required` means the record is latched and needs reauthentication.
19
+ • `statusCredential` on an unknown or revoked handle RESOLVES, it does not throw: it
20
+ returns `ready: false, lastErrorCode: 'not_found'` with `recordVersion`, `credentialId`
21
+ and `stalePending` absent. Since 0.5.0 `CredentialStatus.recordVersion` is optional for
22
+ that reason; handle `undefined` rather than defaulting it to a number, which would read
23
+ as a version older than every real one.
19
24
  • Unknown server error classes are bounded to `transient`; callers must retry them
20
25
  rather than treating a forward-compatible class as permanent.
21
26
 
package/dist/wire.d.ts CHANGED
@@ -15,6 +15,14 @@ export type ClaustrumClientOptions = {
15
15
  /**
16
16
  * Credential material plus optional non-secret identity metadata. Missing wire fields remain
17
17
  * `undefined`; a present non-string identity value rejects the response as invalid.
18
+ *
19
+ * An absent identity field means the vault had nothing to assert — NOT that identity failed.
20
+ * Consumers must not collapse absence into mismatch: assert on presence, refuse on a present
21
+ * difference, and serve (log, if it matters) when the field is missing. Absence proves neither
22
+ * the right identity nor the wrong one, so treating it as a failure converts a vault that could
23
+ * not answer into a consumer that cannot serve. The vault records no per-read success (at most
24
+ * the first scoped read per principal), and nothing at all when a consumer declines to serve, so
25
+ * an outage caused that way is visible only to the consumer that caused it.
18
26
  */
19
27
  export type ServedCredential = {
20
28
  material: string;
@@ -30,6 +38,12 @@ export type ServedCredential = {
30
38
  /**
31
39
  * Provider account identity the served token executes under. Account-scoped routing joins this
32
40
  * value with `recordVersion`; it is neither the operator's credential label nor the bearer handle.
41
+ *
42
+ * BEST-EFFORT, NEVER GUARANTEED, including for JWT providers. The vault parses the claim live
43
+ * from the served access token and falls back to stored login-time identity; both legs can be
44
+ * empty, and the serve path yields nothing rather than fabricating an id. It is absent when the
45
+ * provider has no known claim, the token does not carry one, or no identity was captured at
46
+ * login. Do not gate serving on its presence.
33
47
  */
34
48
  accountId?: string;
35
49
  /** Non-secret account display metadata captured at login. */
@@ -37,14 +51,43 @@ export type ServedCredential = {
37
51
  /** Non-secret organization or workspace display metadata captured at login. */
38
52
  orgName?: string;
39
53
  };
54
+ /**
55
+ * Non-secret health for one handle, read without minting anything.
56
+ *
57
+ * An unresolved handle (unknown or revoked) is an ordinary answer, not an error: it decodes
58
+ * as `ready: false, lastErrorCode: 'not_found'` with `credentialId`, `recordVersion` and
59
+ * `stalePending` all absent. The vault omits them on purpose so a probe learns nothing about
60
+ * what exists and no placeholder value is asserted about a record it never saw.
61
+ */
40
62
  export type CredentialStatus = {
41
63
  ready: boolean;
42
64
  lastErrorCode: string | null;
43
65
  leaseHeld: boolean;
44
- recordVersion: number;
66
+ /**
67
+ * Operator-chosen record label the handle resolved to, for verifying a binding the caller
68
+ * already holds. Not a routing key. Absent when the handle did not resolve.
69
+ */
70
+ credentialId?: string;
71
+ /**
72
+ * The record's change cursor, present only when the handle resolved to a record.
73
+ *
74
+ * Optional, and OMITTED from the object rather than defaulted, because there is no honest
75
+ * default: 0 or -1 would compare as older than every real version, so a poller would read a
76
+ * revoked handle as a change it had yet to see, forever. With the key absent, TypeScript
77
+ * refuses arithmetic or ordering on it until the caller has handled `undefined`, and
78
+ * `'recordVersion' in status` is false. Never replace absence with a number (`?? 0`).
79
+ */
80
+ recordVersion?: number;
45
81
  stalePending?: boolean;
46
82
  };
47
83
  export type ClaustrumReporterSource = 'direct' | 'relay_status_field' | 'relay_message_parse';
84
+ /**
85
+ * Exported for the golden-reply test only; `index.ts` re-exports by name, so this does not
86
+ * widen the published surface.
87
+ */
88
+ export declare function decodeCredential(response: unknown, logUnknownClass: (errorClass: string) => void): ServedCredential;
89
+ /** Exported for the golden-reply test only, like `decodeCredential`. */
90
+ export declare function decodeStatus(response: unknown, logUnknownClass: (errorClass: string) => void): CredentialStatus;
48
91
  export declare class ClaustrumClient {
49
92
  #private;
50
93
  private constructor();
package/dist/wire.js CHANGED
@@ -15,7 +15,11 @@ function asRecordVersion(value) {
15
15
  function isOptionalString(value) {
16
16
  return value === undefined || typeof value === 'string';
17
17
  }
18
- function decodeCredential(response, logUnknownClass) {
18
+ /**
19
+ * Exported for the golden-reply test only; `index.ts` re-exports by name, so this does not
20
+ * widen the published surface.
21
+ */
22
+ export function decodeCredential(response, logUnknownClass) {
19
23
  if (hasCredentialError(response))
20
24
  throw asCredentialError(response, 'invalid_response', logUnknownClass);
21
25
  const result = isRecord(response) && isRecord(response.result) ? response.result : undefined;
@@ -61,15 +65,22 @@ function decodeCredential(response, logUnknownClass) {
61
65
  orgName,
62
66
  };
63
67
  }
64
- function decodeStatus(response, logUnknownClass) {
68
+ /** Exported for the golden-reply test only, like `decodeCredential`. */
69
+ export function decodeStatus(response, logUnknownClass) {
65
70
  if (hasCredentialError(response))
66
71
  throw asCredentialError(response, 'invalid_response', logUnknownClass);
67
72
  const result = isRecord(response) && isRecord(response.result) ? response.result : undefined;
68
- const recordVersion = asRecordVersion(result?.record_version);
73
+ // Absent is a valid reply (the handle did not resolve); a PRESENT value that is not a
74
+ // non-negative integer is malformed. The producer omits the key rather than nulling it,
75
+ // so an explicit null is refused too.
76
+ const rawRecordVersion = result?.record_version;
77
+ const recordVersion = rawRecordVersion === undefined ? undefined : asRecordVersion(rawRecordVersion);
78
+ const credentialId = result?.credential_id;
69
79
  if (typeof result?.ready !== 'boolean' ||
70
80
  (result?.last_error_code !== undefined && result?.last_error_code !== null && typeof result?.last_error_code !== 'string') ||
71
81
  typeof result?.lease_held !== 'boolean' ||
72
- recordVersion === undefined ||
82
+ (rawRecordVersion !== undefined && recordVersion === undefined) ||
83
+ !isOptionalString(credentialId) ||
73
84
  (result?.stale_pending !== undefined && typeof result.stale_pending !== 'boolean')) {
74
85
  throw asCredentialError(response, 'invalid_status', logUnknownClass);
75
86
  }
@@ -77,7 +88,8 @@ function decodeStatus(response, logUnknownClass) {
77
88
  ready: result.ready,
78
89
  lastErrorCode: result.last_error_code ?? null,
79
90
  leaseHeld: result.lease_held,
80
- recordVersion,
91
+ ...(credentialId === undefined ? {} : { credentialId }),
92
+ ...(recordVersion === undefined ? {} : { recordVersion }),
81
93
  ...(result.stale_pending === undefined ? {} : { stalePending: result.stale_pending }),
82
94
  };
83
95
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/claustrum-client",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",