DX-1211: Auth interface docstrings (prerequisites, side-effects, failure modes)#2242
DX-1211: Auth interface docstrings (prerequisites, side-effects, failure modes)#2242umair-ably wants to merge 11 commits into
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
4d45490 to
c81b62a
Compare
m-hulbert
left a comment
There was a problem hiding this comment.
Just a few suggestions on splitting up long sentences and large walls of text into paragraphs, but see what you think.
|
|
||
| /** | ||
| * Instructs the library to get a new token immediately. When using the realtime client, it upgrades the current realtime connection to use the new token, or if not connected, initiates a connection to Ably, once the new token has been obtained. Also stores any {@link TokenParams} and {@link AuthOptions} passed in as the new defaults, to be used for all subsequent implicit or explicit token requests. Any {@link TokenParams} and {@link AuthOptions} objects passed in entirely replace, as opposed to being merged with, the current client library saved values. | ||
| * Instructs the library to get a new token immediately. On a realtime client it re-authenticates the live connection, or initiates a connection if not connected, and the returned promise resolves only once the new token has taken effect on a `connected` connection, rejecting with an {@link ErrorInfo} if re-authentication fails or the connection cannot be (re)established. The client must be able to issue tokens, so a `key`, `authUrl`, or `authCallback` must be configured in {@link ClientOptions}, otherwise the call rejects with an {@link ErrorInfo}; `authorize()` cannot change the API key, so passing an `authOptions.key` that differs from the one the client was constructed with also rejects. Any {@link TokenParams} and {@link AuthOptions} passed in are stored as the new defaults for all subsequent token requests and entirely replace, rather than merge with, the current saved values. |
There was a problem hiding this comment.
| * Instructs the library to get a new token immediately. On a realtime client it re-authenticates the live connection, or initiates a connection if not connected, and the returned promise resolves only once the new token has taken effect on a `connected` connection, rejecting with an {@link ErrorInfo} if re-authentication fails or the connection cannot be (re)established. The client must be able to issue tokens, so a `key`, `authUrl`, or `authCallback` must be configured in {@link ClientOptions}, otherwise the call rejects with an {@link ErrorInfo}; `authorize()` cannot change the API key, so passing an `authOptions.key` that differs from the one the client was constructed with also rejects. Any {@link TokenParams} and {@link AuthOptions} passed in are stored as the new defaults for all subsequent token requests and entirely replace, rather than merge with, the current saved values. | |
| * Instructs the library to get a new token immediately. | |
| * | |
| * On a realtime client it re-authenticates the live connection, or initiates a connection if not currently connected. The returned promise resolves only once the new token has taken effect on a `connected` connection. It rejects with an {@link ErrorInfo} if re-authentication fails or the connection cannot be (re)established. | |
| * | |
| * The client must be able to issue tokens, so a `key`, `authUrl`, or `authCallback` must be configured in {@link ClientOptions}. If one of these isn't configured the call rejects with an {@link ErrorInfo}. | |
| * | |
| * `authorize()` cannot change the API key, so passing an `authOptions.key` that differs from the one the client was constructed with will be rejected. | |
| * | |
| * Any {@link TokenParams} and {@link AuthOptions} passed in are stored as the new defaults for all subsequent token requests and entirely replace, rather than merge with, the current saved values. |
AFAIK you can create paragraphs using empty lines, which helps break up this wall of text. I'd also push for shorter sentences where possible as some of those got quite convoluted.
| ): never; | ||
| /** | ||
| * Creates and signs an Ably {@link TokenRequest} based on the specified (or if none specified, the client library stored) {@link TokenParams} and {@link AuthOptions}. Note this can only be used when the API `key` value is available locally. Otherwise, the Ably {@link TokenRequest} must be obtained from the key owner. Use this to generate an Ably {@link TokenRequest} in order to implement an Ably Token request callback for use by other clients. Both {@link TokenParams} and {@link AuthOptions} are optional. When omitted or `null`, the default token parameters and authentication options for the client library are used, as specified in the {@link ClientOptions} when the client library was instantiated, or later updated with an explicit `authorize` request. Values passed in are used instead of, rather than being merged with, the default values. To understand why an Ably {@link TokenRequest} may be issued to clients in favor of a token, see [Token Authentication explained](https://ably.com/docs/core-features/authentication/#token-authentication). | ||
| * Creates and signs an Ably {@link TokenRequest} based on the specified (or if none specified, the client library stored) {@link TokenParams} and {@link AuthOptions}. Use this to implement an Ably Token request callback for use by other clients. An API `key` value must be available locally to sign the request, supplied either in the client's {@link ClientOptions} or as `key` in the `authOptions` argument; without one the call rejects with an {@link ErrorInfo}, since a token-authenticated client cannot construct token requests itself and must instead obtain the {@link TokenRequest} from the key owner. Both {@link TokenParams} and {@link AuthOptions} are optional; when omitted or `null`, the client library's stored defaults (those set at construction or by a later `authorize` call) are used, and any values passed in replace, rather than merge with, those defaults. |
There was a problem hiding this comment.
| * Creates and signs an Ably {@link TokenRequest} based on the specified (or if none specified, the client library stored) {@link TokenParams} and {@link AuthOptions}. Use this to implement an Ably Token request callback for use by other clients. An API `key` value must be available locally to sign the request, supplied either in the client's {@link ClientOptions} or as `key` in the `authOptions` argument; without one the call rejects with an {@link ErrorInfo}, since a token-authenticated client cannot construct token requests itself and must instead obtain the {@link TokenRequest} from the key owner. Both {@link TokenParams} and {@link AuthOptions} are optional; when omitted or `null`, the client library's stored defaults (those set at construction or by a later `authorize` call) are used, and any values passed in replace, rather than merge with, those defaults. | |
| * Creates and signs an Ably {@link TokenRequest} based on the specified {@link TokenParams} and {@link AuthOptions}. If no `TokenRequest` or `TokenParams` are specified it uses those previously stored by the library. Use this to implement an Ably Token request callback for use by other clients. | |
| * | |
| * An API `key` value must be available locally to sign the request, supplied either in the client's {@link ClientOptions} or as `key` in the `authOptions` argument. Without a `key` the call rejects with an {@link ErrorInfo}, since a token-authenticated client cannot construct token requests itself and must instead obtain the {@link TokenRequest} from the key owner. | |
| * | |
| * Both {@link TokenParams} and {@link AuthOptions} are optional. When omitted or `null`, the client library's stored defaults (those set at construction or by a later `authorize()` call) are used, and any values passed in replace, rather than merge with, those defaults. |
| ): never; | ||
| /** | ||
| * Calls the `requestToken` REST API endpoint to obtain an Ably Token according to the specified {@link TokenParams} and {@link AuthOptions}. Both {@link TokenParams} and {@link AuthOptions} are optional. When omitted or `null`, the default token parameters and authentication options for the client library are used, as specified in the {@link ClientOptions} when the client library was instantiated, or later updated with an explicit `authorize` request. Values passed in are used instead of, rather than being merged with, the default values. To understand why an Ably {@link TokenRequest} may be issued to clients in favor of a token, see [Token Authentication explained](https://ably.com/docs/core-features/authentication/#token-authentication). | ||
| * Calls the `requestToken` REST API endpoint to obtain an Ably Token according to the specified {@link TokenParams} and {@link AuthOptions}. Both are optional; when omitted or `null`, the client's stored defaults are used, as specified in the {@link ClientOptions} at instantiation or later updated by an `authorize` request, and any values passed in are used instead of, rather than merged with, those defaults. The client must have a usable way to obtain a token, so the resolved {@link AuthOptions} must include one of `authCallback`, `authUrl`, or `key`; a client given only a literal token or `tokenDetails` with no renewal mechanism cannot request a new token and the call rejects with an {@link ErrorInfo}. |
There was a problem hiding this comment.
| * Calls the `requestToken` REST API endpoint to obtain an Ably Token according to the specified {@link TokenParams} and {@link AuthOptions}. Both are optional; when omitted or `null`, the client's stored defaults are used, as specified in the {@link ClientOptions} at instantiation or later updated by an `authorize` request, and any values passed in are used instead of, rather than merged with, those defaults. The client must have a usable way to obtain a token, so the resolved {@link AuthOptions} must include one of `authCallback`, `authUrl`, or `key`; a client given only a literal token or `tokenDetails` with no renewal mechanism cannot request a new token and the call rejects with an {@link ErrorInfo}. | |
| * Calls the `requestToken` REST API endpoint to obtain an Ably Token according to the specified {@link TokenParams} and {@link AuthOptions}. Both properties are optional. When omitted or `null`, the client's stored defaults are used, as specified in the {@link ClientOptions} at instantiation or later updated by an `authorize()` request. Any values passed in are used instead of, rather than merged with, those defaults. | |
| * | |
| * The client must have a way to obtain a token, so the resolved {@link AuthOptions} must include one of `authCallback`, `authUrl`, or `key`. A client given only a literal token or `tokenDetails` with no renewal mechanism cannot request a new token and the call rejects with an {@link ErrorInfo}. |
On the 2nd paragraph I've made here - unsure if those 2 sentences are supposed to be related. They were separated by a semi-colon but I'm not sure they are.
Small note here on inconsistency between the descriptions too in how we refer to the default values and authorize().
Could argue whether the user needs to know that it's calling the REST API, but I won't go and rewrite everything as that's not the point 🙂
| /** | ||
| * Revokes the tokens specified by the provided array of {@link TokenRevocationTargetSpecifier}s. Only tokens issued by an API key that had revocable tokens enabled before the token was issued can be revoked. See the [token revocation docs](https://ably.com/docs/core-features/authentication#token-revocation) for more information. | ||
| * Revokes the tokens specified by the provided array of {@link TokenRevocationTargetSpecifier}s. | ||
| * The client must be authenticated with an API key (basic auth), not a token; a |
There was a problem hiding this comment.
This reads a bit confusing as it makes it sound like the client you're trying to revoke a token for needs to be authenticated by a key.
7c617fe to
729d8f0
Compare
c81b62a to
75ca7f8
Compare
Apply the docstrings-standards.md review pass to the Auth interface, incorporating m-hulbert's PR #2242 suggestions: - Split walls of text into blank-line paragraphs, shorten sentences, and remove semicolons from descriptions (A1/A2). - authorize: five paragraphs with active reject voice and an ErrorInfo on each failure mode. - createTokenRequest: promote the defaults parenthetical to a sentence and correct the reviewer's "TokenRequest" arg slip (the method takes no TokenRequest parameter). - requestToken/createTokenRequest: one verbatim stored-defaults phrasing, and authorize() with parentheses (C5/F2). - revokeTokens: anchor the basic-auth prerequisite to the calling client to remove the ambiguity m-hulbert flagged (D5). - clientId: drop the inline identified-clients link now that the canonical @see is present (E1). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Declaration only — no runtime use yet. Documented silent-failure paths will read this option in subsequent commits to gate a hint-carrying throw; the default stays `false` in v2.x. Per DXRFC-022 work item B5 the default flips to `true` in v3 with no per-call opt-out. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The guard at line 73 parenthesised `(state === 'attached' && _mode & flag) === 0` so the `=== 0` compared against `(boolean && number)`. It happened to behave correctly — `false === 0` is `false` so the throw skipped when not attached, and the bitwise result === 0 was correct when attached — but the comparison was structural luck rather than the obvious reading of the predicate. Re-parenthesise to `state === 'attached' && (_mode & flag) === 0`. Also emit an always-on warning log adjacent to the throw so the diagnostic fires in the SDK output even when the caller swallows the throw. No silentFailureLogSuffix here because this throw is unconditional (pre-DXRFC-022) and not strictMode-gated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A short suffix appended to silent-failure warning logs emitted when clientOptions.strictMode is off, so the reader knows the same path will throw in a future major version. Co-locating on Logger keeps the import surface tight; callers do `Logger.silentFailureLogSuffix()` next to `Logger.logActionNoStrip(...)`. Log-only by design — the suffix is not put into ErrorInfo.hint, because the hint is also shown when the throw fires (strictMode on), where the suffix would be misleading. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a channel was attached without the presence_subscribe mode, the
server never delivers presence members, so presence.get() resolves to
[] regardless of who is actually present. Today there is no signal
distinguishing "no one is present" from "this client cannot see anyone".
This commit detects the case after ensureAttached() populates the
server-granted mode set, then:
- emits an always-on warning log carrying the hint + a suffix telling
the reader that strictMode will throw in a future major version.
- throws ErrorInfo with err.hint when clientOptions.strictMode === true.
Code 93002 sits next to 93001 (annotation_subscribe missing) in the
SDK-internal precondition class. It would also be defensible to use
40160 (server-side capability denied), but the hint-coverage rubric
already pins 40160 to the "no auth options" hint shape, so a second
40160 throw site would either weaken that pin or need a rubric refactor.
Suspended-state and {waitForSync: false} paths return earlier and are
unaffected.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a channel was attached without the subscribe mode, the server never delivers messages to the listener, so channel.subscribe() appears to succeed but no callback ever fires. This commit closes the gap symmetrically to presence.get() in the previous commit: - After the implicit attach completes (RTL7g), if subscribe mode was not granted by the server, emit an always-on warning log carrying the hint + the future-throw suffix. - One-shot per channel: the warning fires once per attach cycle to keep noise down on long-lived listeners. Reset _silentSubscribeWarned on the ATTACHED message so a channels.release() + re-attach with corrected modes restores signalling. - Throw ErrorInfo (code 93003) when clientOptions.strictMode === true. attachOnSubscribe: false is out of scope — the check requires an attach to have populated _mode. Document this caveat separately if needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Terser, technical error messages for 93002/93003 - Hints now offer setOptions() or omitting modes + capability check; drop confusing channel.modes note - Keep listener registered on strict-mode throw (matches existing subscribe semantics) - Remove ticket IDs from test names Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…grade The 93001 hint lumped two distinct failure modes under "if the subsequent attach is rejected": a missing namespace rule (Mutable Messages) does reject the attach, but a missing annotation-subscribe capability does NOT - the server silently drops the mode and this same error re-fires. Confirmed by live sandbox traces and the canonical error registry. Reword to key off the symptom the caller observes: attach rejected => enable the namespace rule; attach succeeds but annotations still undelivered => grant the capability. Addresses review feedback that the hint was presumptuous and over-enumerated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… off colliding 93002 Two fixes to the presence.get()/channel.subscribe() missing-mode errors: - Hint correctness (review feedback): the presence hint named a "presence-subscribe" capability that does not exist. Presence delivery is governed by the "subscribe" capability, matching the subscribe-mode hint. - Error-code collision: 93002 is the canonical server code for "namespace needs Mutable Messages" (ably-common errors.json, faqs.ably.com/error-code-93002). presence.get() reused it client-side. Move presence to 91008 (presence block, next to 91005) and channel.subscribe() to 90009 (channel block); 93xxx is the annotations/mutable-messages block. Both codes are new on this unreleased branch, so the renumber is non-breaking. Reserved in ably/ably-common#345. Update the two tests asserting them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ts, failure modes) Apply docstringRules.md to the public Auth interface in ably.d.ts so silent and architectural call-site prerequisites are discoverable at the call site: - revokeTokens: basic-auth (API key, not token) requirement; non-code "revocable tokens enabled on the key" prerequisite, with a feature-page @see. - requestToken: token-issuing prerequisite (authCallback/authUrl/key); callback contract detail (content-types, size flags) offloaded to the @see. - createTokenRequest: a local API key must be available to sign the request. - authorize: re-authenticates the live connection (resolves once the token takes effect on a connected connection), token-issuing prerequisite, and the RSA10a key-immutability rule (authorize() cannot change the API key). - clientId: adds the canonical @see. Folded prose, no numeric error codes; every behavioural method carries one simple @example and every member an @see to the canonical JS API reference. TypeDoc (treatWarningsAsErrors), eslint, and prettier are clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Apply the docstrings-standards.md review pass to the Auth interface, incorporating m-hulbert's PR #2242 suggestions: - Split walls of text into blank-line paragraphs, shorten sentences, and remove semicolons from descriptions (A1/A2). - authorize: five paragraphs with active reject voice and an ErrorInfo on each failure mode. - createTokenRequest: promote the defaults parenthetical to a sentence and correct the reviewer's "TokenRequest" arg slip (the method takes no TokenRequest parameter). - requestToken/createTokenRequest: one verbatim stored-defaults phrasing, and authorize() with parentheses (C5/F2). - revokeTokens: anchor the basic-auth prerequisite to the calling client to remove the ambiguity m-hulbert flagged (D5). - clientId: drop the inline identified-clients link now that the canonical @see is present (E1). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2a86111 to
7906161
Compare
Applies
docstringRules.mdto the publicAuthinterface inably.d.ts, surfacing the call-site prerequisites and failure modes a caller cannot infer from the type signature (the DX-1211 class of silent/architectural pitfall). Same effort as the already-landed RealtimeChannel/RealtimePresence pass; one subsystem = one PR.Members (5 audited; 3 deprecated v1-callback overloads skipped)
revokeTokens@see)requestTokenauthCallback/authUrl/key); callback-contract detail offloaded to the@seecreateTokenRequestkeymust be available to sign the requestauthorizeconnectedconnection); token-issuing prerequisite; RSA10a key-immutability (authorize()cannot change the API key)clientId@seeConventions
{@link ErrorInfo}").@seeto the canonical JS API reference.src/(file:line) in an adversarial verify pass before applying.Validation
npm run docs(TypeDoc,treatWarningsAsErrors) — clean (all{@link}resolve)eslint ably.d.ts— exit 0 ·prettier --check— cleanReviewer notes / follow-ups
@seeanchors: the canonical reference (ably/docs #3400) is not yet published, so 4 of 5 anchors are convention-derived per §6 and unverified (#revoke-tokensis corroborated).@seeis not validated by TypeDoc, so re-check the slugs when #3400 ships.clientIdchange is cosmetic (@seeonly) — the drafter'snull/wildcard value claims were rejected by verification as inaccurate. Fine to drop if you'd rather treat it as a pure accessor.🤖 Generated with Claude Code