axonium 0.0.0-stage → 0.1.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/LICENSE +27 -0
- package/README.md +157 -2
- package/dist/cjs/auth.cjs +298 -0
- package/dist/cjs/auth.js.map +1 -0
- package/dist/cjs/client.cjs +235 -0
- package/dist/cjs/client.js.map +1 -0
- package/dist/cjs/config.cjs +133 -0
- package/dist/cjs/config.js.map +1 -0
- package/dist/cjs/errors.cjs +482 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/index.cjs +87 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/resources.cjs +181 -0
- package/dist/cjs/resources.js.map +1 -0
- package/dist/cjs/retry.cjs +102 -0
- package/dist/cjs/retry.js.map +1 -0
- package/dist/cjs/sse.cjs +317 -0
- package/dist/cjs/sse.js.map +1 -0
- package/dist/cjs/transport.cjs +306 -0
- package/dist/cjs/transport.js.map +1 -0
- package/dist/cjs/version.cjs +16 -0
- package/dist/cjs/version.js.map +1 -0
- package/dist/esm/auth.d.ts +77 -0
- package/dist/esm/auth.d.ts.map +1 -0
- package/dist/esm/auth.js +293 -0
- package/dist/esm/auth.js.map +1 -0
- package/dist/esm/client.d.ts +202 -0
- package/dist/esm/client.d.ts.map +1 -0
- package/dist/esm/client.js +229 -0
- package/dist/esm/client.js.map +1 -0
- package/dist/esm/config.d.ts +75 -0
- package/dist/esm/config.d.ts.map +1 -0
- package/dist/esm/config.js +129 -0
- package/dist/esm/config.js.map +1 -0
- package/dist/esm/errors.d.ts +389 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +430 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/index.d.ts +24 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/resources.d.ts +167 -0
- package/dist/esm/resources.d.ts.map +1 -0
- package/dist/esm/resources.js +170 -0
- package/dist/esm/resources.js.map +1 -0
- package/dist/esm/retry.d.ts +72 -0
- package/dist/esm/retry.d.ts.map +1 -0
- package/dist/esm/retry.js +96 -0
- package/dist/esm/retry.js.map +1 -0
- package/dist/esm/sse.d.ts +83 -0
- package/dist/esm/sse.d.ts.map +1 -0
- package/dist/esm/sse.js +311 -0
- package/dist/esm/sse.js.map +1 -0
- package/dist/esm/transport.d.ts +91 -0
- package/dist/esm/transport.d.ts.map +1 -0
- package/dist/esm/transport.js +299 -0
- package/dist/esm/transport.js.map +1 -0
- package/dist/esm/version.d.ts +13 -0
- package/dist/esm/version.d.ts.map +1 -0
- package/dist/esm/version.js +13 -0
- package/dist/esm/version.js.map +1 -0
- package/docs/api.md +263 -0
- package/docs/migrating-from-python.md +172 -0
- package/package.json +57 -4
- package/src/auth.ts +359 -0
- package/src/client.ts +365 -0
- package/src/config.ts +237 -0
- package/src/errors.ts +581 -0
- package/src/index.ts +106 -0
- package/src/resources.ts +349 -0
- package/src/retry.ts +128 -0
- package/src/sse.ts +333 -0
- package/src/transport.ts +379 -0
- package/src/version.ts +13 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Emeric Espiritu Santiago
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
Additional Terms for Enterprise Use:
|
|
24
|
+
|
|
25
|
+
For commercial or enterprise use, please contact the copyright holder at
|
|
26
|
+
emericespiritusantiago@gmail.com to discuss licensing terms and potential
|
|
27
|
+
remuneration.
|
package/README.md
CHANGED
|
@@ -1,3 +1,158 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Axonium for TypeScript
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The Axonium SDK for the Prometheus inference platform, for Node, Bun, Deno and edge runtimes.
|
|
4
|
+
|
|
5
|
+
> **Status: `0.1.0`, ready to publish and not yet published.** Every resource, streaming, vision, tool
|
|
6
|
+
> calling and structured output; **49 of 49** shared contract cases; 77 unit tests and 16 integration
|
|
7
|
+
> tests against a live gateway; five runnable [examples](examples/) and an
|
|
8
|
+
> [API reference](docs/api.md). **9.5 KB** minified and gzipped, against a 50 KB budget.
|
|
9
|
+
>
|
|
10
|
+
> Not here yet: OpenTelemetry and a logging hook (`0.2.0`), `usage.export` (in no SDK of this family),
|
|
11
|
+
> and `X-Prometheus-Ignored-Parameters` on a success (held until all five SDKs agree on the shape). Bun,
|
|
12
|
+
> Deno and edge are **untested** — only Node 20, 22 and 24 are in CI, and claiming the rest because this
|
|
13
|
+
> only uses `fetch` would be the kind of unmeasured assertion this repository keeps catching.
|
|
14
|
+
>
|
|
15
|
+
> Requested in
|
|
16
|
+
> `apeiron_axonium_prometheus/solicitud-axonium-sdk-typescript.md`; see that channel for scope and
|
|
17
|
+
> dates.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { Transport, resolveConfig } from "axonium";
|
|
21
|
+
|
|
22
|
+
const transport = new Transport(resolveConfig()); // reads AXONIUM_GATEWAY_BASE_URL, …
|
|
23
|
+
|
|
24
|
+
const catalog = await transport.sendJSON("GET", "/v1/models", {});
|
|
25
|
+
const answer = await transport.sendJSON("POST", "/v1/chat/completions", {
|
|
26
|
+
model: "qwen3-0.6b",
|
|
27
|
+
body: { model: "qwen3-0.6b", messages: [{ role: "user", content: "hola" }] },
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`Transport` is the layer the resource methods will sit on; it is exported because it is useful on its
|
|
32
|
+
own and because nothing about it should be a surprise later.
|
|
33
|
+
|
|
34
|
+
## What is decided, and why
|
|
35
|
+
|
|
36
|
+
**Zero runtime dependencies, and a test that says so.** `fetch` is the platform's. A transitive
|
|
37
|
+
dependency is how a 50 KB budget becomes 400 KB and how an edge build breaks, and both get discovered
|
|
38
|
+
by the consumer rather than here — so `test/package.test.ts` fails if `dependencies` is ever
|
|
39
|
+
non-empty, and if anything under `src/` imports a `node:` module.
|
|
40
|
+
|
|
41
|
+
**`fetch` is injected, not patched.** `options.fetch` exists so the contract corpus can be replayed
|
|
42
|
+
against a function. That is why this package has no HTTP-mocking dev dependency, and a test refuses
|
|
43
|
+
one: with a mocking library, what the suite exercises stops being the real transport.
|
|
44
|
+
|
|
45
|
+
**The source is erasable TypeScript.** `erasableSyntaxOnly` is on, so no parameter properties, enums
|
|
46
|
+
or decorators — nothing that compiles to code. The reason is concrete rather than stylistic: Node
|
|
47
|
+
runs this package's own `.ts` with `--experimental-strip-types`, so the whole suite needs no build
|
|
48
|
+
step and no test runner. Writing one parameter property is what revealed this, and a compiler flag is
|
|
49
|
+
cheaper than remembering.
|
|
50
|
+
|
|
51
|
+
**A credential is refused where this cannot establish it is on a server.** Not via
|
|
52
|
+
`typeof window !== "undefined"` — jsdom defines `window`, so that fires in anyone's vitest suite, and
|
|
53
|
+
a guard with false positives in CI is a guard somebody disables. It looks for positive evidence of a
|
|
54
|
+
server runtime instead, so an environment nobody anticipated reads as unknown rather than as safe.
|
|
55
|
+
|
|
56
|
+
The rule it enforces is about **whose** credential it is, not where the code runs. A `client_id` is
|
|
57
|
+
the principal that model grants and every usage row are keyed to, so an integrator's credential must
|
|
58
|
+
never reach a machine its users control — while an end client's **own** credential may live on their
|
|
59
|
+
own device, which the platform supports explicitly. A bundle cannot tell those apart, so the default
|
|
60
|
+
refuses and `allowInsecureCredential` is how a caller states which case theirs is.
|
|
61
|
+
|
|
62
|
+
**`request` defaults to 600 s, deliberately.** The gateway allows its backends 600 s and some image
|
|
63
|
+
backends take 2–8 minutes. A client timeout shorter than the server's, combined with a retry, is a
|
|
64
|
+
known failure mode: the backend keeps computing and the retry queues a second expensive generation.
|
|
65
|
+
**An edge runtime cannot wait that long** — that is a real conflict rather than a tuning question, and
|
|
66
|
+
a deployment that cannot afford it should say so instead of meeting it as a truncated request.
|
|
67
|
+
|
|
68
|
+
## What the transport does
|
|
69
|
+
|
|
70
|
+
**Retries only what the contract says reached no model.** This API deduplicates nothing server-side,
|
|
71
|
+
so a retried generation is a _new billable one_ rather than a replay — which makes an over-eager
|
|
72
|
+
policy expensive rather than merely noisy. Three attempts, 1s base, 60s cap, full jitter. An
|
|
73
|
+
`idempotencyKey` is what turns a repeat into a replay, and every call accepts one.
|
|
74
|
+
|
|
75
|
+
**A client timeout is never retried.** The backend is probably still generating, so a retry queues a
|
|
76
|
+
second expensive generation on top of the first rather than resuming it.
|
|
77
|
+
|
|
78
|
+
**`Retry-After` is honoured verbatim**, because the gateway computes it from the real reset time or
|
|
79
|
+
the breaker's expected recovery. It is still capped: a wait longer than `maxBackoff` is handed to the
|
|
80
|
+
caller rather than slept through, since blocking for ten minutes is a scheduling decision that belongs
|
|
81
|
+
to the application.
|
|
82
|
+
|
|
83
|
+
**Cooldowns are keyed by model, not by gateway.** `backend-unavailable` means every replica of _that_
|
|
84
|
+
model is out while others on the same gateway keep serving.
|
|
85
|
+
|
|
86
|
+
**A 401 refreshes the token once, then gives up.** Bounded deliberately: the condition compares the
|
|
87
|
+
rejected token with the one just used, which is false again after every refresh, so without a counter
|
|
88
|
+
a gateway answering 401 to everything loops forever.
|
|
89
|
+
|
|
90
|
+
**Correlation ids are read from the body and then the headers.** The fallback is load-bearing: a body
|
|
91
|
+
that is not an envelope at all still arrives — an HTML page from a proxy that never reached the
|
|
92
|
+
gateway — and there the ids exist only in the headers, if anywhere.
|
|
93
|
+
|
|
94
|
+
## The error taxonomy
|
|
95
|
+
|
|
96
|
+
One class per catalogued `type` suffix, dispatched on the **last path segment** of the problem-details
|
|
97
|
+
`type` URI. An unknown suffix falls back by status rather than throwing: the catalogue grows, and an
|
|
98
|
+
error documented on a Tuesday must not become a parse failure in a version already published.
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
try {
|
|
102
|
+
/* … */
|
|
103
|
+
} catch (err) {
|
|
104
|
+
if (err instanceof ForbiddenError) console.log(err.detail, err.meta.requestId);
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Two deliberate shapes:
|
|
109
|
+
|
|
110
|
+
- **`OAuthError` does not extend `APIError`.** The token endpoint answers RFC 6749
|
|
111
|
+
`{error, error_description}`, not problem+json, and the remedies differ: an inference failure may be
|
|
112
|
+
worth retrying with the same credential, a credential failure never is. Both extend `AxoniumError`,
|
|
113
|
+
so one `catch` still covers the package.
|
|
114
|
+
- **`PredictBackendRejectedError.retryable` is derived from the status**, because the pass-through
|
|
115
|
+
route keeps the _engine's_ status: one suffix covers a `422` that will never succeed and a `429`
|
|
116
|
+
that will.
|
|
117
|
+
|
|
118
|
+
## Documentation
|
|
119
|
+
|
|
120
|
+
- **[`docs/api.md`](docs/api.md)** — every export, including all 34 error classes against their `type`
|
|
121
|
+
suffix. A test asserts that nothing is exported without an entry.
|
|
122
|
+
- **[`docs/migrating-from-python.md`](docs/migrating-from-python.md)** — call by call, including the three
|
|
123
|
+
things Python has that this does not and why.
|
|
124
|
+
- **[`examples/`](examples/)** — chat, streaming from a Next.js Route Handler, vision, tool calling, and
|
|
125
|
+
governed mode. They import `axonium` by name, so they typecheck against the **published** `.d.ts`
|
|
126
|
+
rather than against `src/`.
|
|
127
|
+
|
|
128
|
+
## A custom certificate authority
|
|
129
|
+
|
|
130
|
+
There is no `caBundle` option and there cannot be one: `fetch` has no option for a CA, and reaching one
|
|
131
|
+
means either an `undici` dependency or a `node:` import — each breaking a requirement this package was
|
|
132
|
+
built to. Use the runtime's own mechanism:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
NODE_EXTRA_CA_CERTS=/path/to/ca.pem node app.js # Node, Bun
|
|
136
|
+
deno run --cert /path/to/ca.pem app.ts # Deno
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Development
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npm install
|
|
143
|
+
npm test # node --test, no runner, no build step (needs Node 22+)
|
|
144
|
+
npm run build # ESM + CJS + .d.ts
|
|
145
|
+
npm run lint # prettier --check, plus tsc on the package and on the examples
|
|
146
|
+
|
|
147
|
+
AXONIUM_INTEGRATION=1 AXONIUM_GATEWAY_BASE_URL=… AXONIUM_CLIENT_ID=… AXONIUM_CLIENT_SECRET=… \
|
|
148
|
+
npm run test:integration
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The integration suite is **skipped without credentials and never runs in CI** — it spends real
|
|
152
|
+
inference, so a contributor who has not opted in must not pay for it. It picks models by **modality**
|
|
153
|
+
from the live catalogue rather than hardcoding ids, because a fixed id fails on every deployment that
|
|
154
|
+
does not serve it and that failure reads as a broken SDK.
|
|
155
|
+
|
|
156
|
+
The published package supports Node 20+. Node 20 cannot strip types, so the suite above proves
|
|
157
|
+
nothing about it — CI runs the **built** artifact there instead, which is what a consumer on 20
|
|
158
|
+
installs.
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TokenManager = void 0;
|
|
4
|
+
exports.decodeClaims = decodeClaims;
|
|
5
|
+
const errors_ts_1 = require("./errors.cjs");
|
|
6
|
+
const version_ts_1 = require("./version.cjs");
|
|
7
|
+
/** Refresh once this much of the token's life has gone. */
|
|
8
|
+
const REFRESH_AHEAD_RATIO = 0.8;
|
|
9
|
+
/** …or this little is left, whichever comes first. */
|
|
10
|
+
const REFRESH_AHEAD_MIN_SECONDS = 30;
|
|
11
|
+
/**
|
|
12
|
+
* A ceiling on any advertised lifetime.
|
|
13
|
+
*
|
|
14
|
+
* An `expires_in` of a year is a misconfiguration rather than a gift, and honouring it would mean
|
|
15
|
+
* holding a token that the gateway has long since forgotten, failing every request until the process
|
|
16
|
+
* restarts.
|
|
17
|
+
*/
|
|
18
|
+
const MAX_PLAUSIBLE_TTL_SECONDS = 86_400;
|
|
19
|
+
/**
|
|
20
|
+
* Decodes a JWT's payload without verifying it.
|
|
21
|
+
*
|
|
22
|
+
* **Not verification, and it must not be used as such.** The signature is the gateway's to check; this
|
|
23
|
+
* reads the claims so a caller can see which `client_id` and scopes they are operating as, and so the
|
|
24
|
+
* token's own `exp` can be used as a second reading of its lifetime.
|
|
25
|
+
*
|
|
26
|
+
* Done by hand because the alternative is a dependency, and because all that is needed is one
|
|
27
|
+
* base64url segment. A malformed token yields empty claims rather than throwing: the gateway decides
|
|
28
|
+
* whether a token is good, and a decode failure here must not become the reason a request never left.
|
|
29
|
+
*/
|
|
30
|
+
function decodeClaims(accessToken) {
|
|
31
|
+
const empty = {
|
|
32
|
+
subject: undefined,
|
|
33
|
+
authorizedParty: undefined,
|
|
34
|
+
scope: [],
|
|
35
|
+
expiresAt: undefined,
|
|
36
|
+
issuedAt: undefined,
|
|
37
|
+
raw: {},
|
|
38
|
+
};
|
|
39
|
+
const parts = accessToken.split(".");
|
|
40
|
+
if (parts.length !== 3)
|
|
41
|
+
return empty;
|
|
42
|
+
const payload = parts[1];
|
|
43
|
+
if (!payload)
|
|
44
|
+
return empty;
|
|
45
|
+
let json;
|
|
46
|
+
try {
|
|
47
|
+
const padded = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
48
|
+
const binary = atob(padded + "=".repeat((4 - (padded.length % 4)) % 4));
|
|
49
|
+
// atob gives latin1; a claim with a non-ASCII character would otherwise come back mangled.
|
|
50
|
+
json = new TextDecoder().decode(Uint8Array.from(binary, (c) => c.charCodeAt(0)));
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return empty;
|
|
54
|
+
}
|
|
55
|
+
let raw;
|
|
56
|
+
try {
|
|
57
|
+
const parsed = JSON.parse(json);
|
|
58
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
59
|
+
return empty;
|
|
60
|
+
raw = parsed;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return empty;
|
|
64
|
+
}
|
|
65
|
+
const scope = raw["scope"];
|
|
66
|
+
const text = (key) => typeof raw[key] === "string" ? raw[key] : undefined;
|
|
67
|
+
return {
|
|
68
|
+
subject: text("sub"),
|
|
69
|
+
authorizedParty: text("azp"),
|
|
70
|
+
scope: typeof scope === "string" ? scope.split(" ").filter(Boolean) : [],
|
|
71
|
+
expiresAt: typeof raw["exp"] === "number" ? raw["exp"] : undefined,
|
|
72
|
+
issuedAt: typeof raw["iat"] === "number" ? raw["iat"] : undefined,
|
|
73
|
+
raw,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* How long a token is really good for, in seconds.
|
|
78
|
+
*
|
|
79
|
+
* `expires_in` is the server's own answer and is normally used as-is. When the response's `Date`
|
|
80
|
+
* header and the token's own `exp` are both present, their difference is a **second, independent
|
|
81
|
+
* reading of the same lifetime** — and a skew-free one, because both come from the server's clock
|
|
82
|
+
* rather than being compared against ours. The platform confirmed this is the right anchor.
|
|
83
|
+
*
|
|
84
|
+
* The shorter wins. Disagreement should not happen, and treating a token as expiring sooner only
|
|
85
|
+
* costs an early refresh, while treating it as living longer costs a request.
|
|
86
|
+
*/
|
|
87
|
+
function effectiveLifetime(headers, accessToken, expiresIn) {
|
|
88
|
+
const date = headers.get("Date");
|
|
89
|
+
const exp = decodeClaims(accessToken).expiresAt;
|
|
90
|
+
if (!date || exp === undefined)
|
|
91
|
+
return expiresIn;
|
|
92
|
+
const serverNow = Date.parse(date);
|
|
93
|
+
if (Number.isNaN(serverNow))
|
|
94
|
+
return expiresIn;
|
|
95
|
+
const serverRemaining = exp - serverNow / 1000;
|
|
96
|
+
// Already expired by the server's own reckoning: fail fast rather than spend a request finding out.
|
|
97
|
+
if (serverRemaining <= 0)
|
|
98
|
+
return 0;
|
|
99
|
+
return Math.min(expiresIn, serverRemaining);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Holds the credential and hands out tokens.
|
|
103
|
+
*
|
|
104
|
+
* **Concurrent callers share one in-flight refresh.** A promise is kept rather than a lock, which is
|
|
105
|
+
* the JavaScript shape of double-checked locking: the second caller awaits the first one's request
|
|
106
|
+
* instead of starting its own. Without it, a cold client answering ten simultaneous requests would
|
|
107
|
+
* send ten token requests, and nine of them would be charged against the rate-limit budget for
|
|
108
|
+
* nothing.
|
|
109
|
+
*/
|
|
110
|
+
class TokenManager {
|
|
111
|
+
config;
|
|
112
|
+
provider;
|
|
113
|
+
current;
|
|
114
|
+
inFlight;
|
|
115
|
+
/** The last token a provider gave us, so a 401 can tell the provider *which* one was rejected. */
|
|
116
|
+
lastProvided;
|
|
117
|
+
constructor(config) {
|
|
118
|
+
this.config = config;
|
|
119
|
+
this.provider = config.tokenProvider;
|
|
120
|
+
}
|
|
121
|
+
/** The claims of the token currently held, or `undefined` before the first one is obtained. */
|
|
122
|
+
get claims() {
|
|
123
|
+
const token = this.current?.accessToken ?? this.lastProvided;
|
|
124
|
+
return token === undefined ? undefined : decodeClaims(token);
|
|
125
|
+
}
|
|
126
|
+
/** The scope the gateway granted, which may be narrower than the one requested. */
|
|
127
|
+
get grantedScope() {
|
|
128
|
+
return this.current?.scope ?? this.claims?.scope ?? [];
|
|
129
|
+
}
|
|
130
|
+
/** A token to send now, fetching or refreshing if the one held is spent. */
|
|
131
|
+
async token() {
|
|
132
|
+
if (this.provider) {
|
|
133
|
+
const token = await this.provider.token();
|
|
134
|
+
this.lastProvided = token;
|
|
135
|
+
return token;
|
|
136
|
+
}
|
|
137
|
+
if (this.current && !this.needsRefresh(this.current))
|
|
138
|
+
return this.current.accessToken;
|
|
139
|
+
return (await this.obtain()).accessToken;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Discards the token the gateway just rejected and gets another, exactly once per rejection.
|
|
143
|
+
*
|
|
144
|
+
* Compares against what was rejected rather than refreshing unconditionally: when several
|
|
145
|
+
* concurrent requests are all rejected with the same stale token, the first one's replacement
|
|
146
|
+
* serves them all, and the rest must not each throw away a token that is already good.
|
|
147
|
+
*/
|
|
148
|
+
async refreshAfterRejection(rejected) {
|
|
149
|
+
if (this.provider) {
|
|
150
|
+
const token = await this.provider.refresh(rejected);
|
|
151
|
+
if (token === rejected) {
|
|
152
|
+
throw new errors_ts_1.ConfigurationError("The tokenProvider returned the same token the gateway had just rejected, so retrying " +
|
|
153
|
+
"would fail identically. refresh() must obtain a new one.");
|
|
154
|
+
}
|
|
155
|
+
this.lastProvided = token;
|
|
156
|
+
return token;
|
|
157
|
+
}
|
|
158
|
+
if (this.current && this.current.accessToken !== rejected) {
|
|
159
|
+
return this.current.accessToken;
|
|
160
|
+
}
|
|
161
|
+
this.current = undefined;
|
|
162
|
+
return (await this.obtain()).accessToken;
|
|
163
|
+
}
|
|
164
|
+
needsRefresh(token) {
|
|
165
|
+
const remaining = (token.expiresAt - performance.now()) / 1000;
|
|
166
|
+
return (remaining <= Math.max(token.expiresIn * (1 - REFRESH_AHEAD_RATIO), 0) ||
|
|
167
|
+
remaining <= REFRESH_AHEAD_MIN_SECONDS);
|
|
168
|
+
}
|
|
169
|
+
async obtain() {
|
|
170
|
+
// The second caller joins the first one's request instead of starting another.
|
|
171
|
+
if (this.inFlight)
|
|
172
|
+
return this.inFlight;
|
|
173
|
+
const attempt = this.fetchToken()
|
|
174
|
+
.then((token) => {
|
|
175
|
+
this.current = token;
|
|
176
|
+
return token;
|
|
177
|
+
})
|
|
178
|
+
.finally(() => {
|
|
179
|
+
this.inFlight = undefined;
|
|
180
|
+
});
|
|
181
|
+
this.inFlight = attempt;
|
|
182
|
+
return attempt;
|
|
183
|
+
}
|
|
184
|
+
async fetchToken() {
|
|
185
|
+
const body = new URLSearchParams({
|
|
186
|
+
grant_type: "client_credentials",
|
|
187
|
+
client_id: this.config.clientId,
|
|
188
|
+
client_secret: this.config.clientSecret,
|
|
189
|
+
});
|
|
190
|
+
// Omitted rather than sent empty: an empty `scope` is a request for no scopes at all, while
|
|
191
|
+
// omitting it asks for everything the account holds.
|
|
192
|
+
if (this.config.scope)
|
|
193
|
+
body.set("scope", this.config.scope);
|
|
194
|
+
// Captured before the request so the round trip is charged against the token's life rather than
|
|
195
|
+
// granted as extra margin. performance.now() rather than Date.now() so a system clock adjustment
|
|
196
|
+
// mid-flight cannot move the deadline.
|
|
197
|
+
const issuedAt = performance.now();
|
|
198
|
+
const signal = AbortSignal.timeout(this.config.timeouts.connect + this.config.timeouts.request);
|
|
199
|
+
let response;
|
|
200
|
+
try {
|
|
201
|
+
response = await this.config.fetch(`${this.config.gatewayBaseURL}/oauth2/token`, {
|
|
202
|
+
method: "POST",
|
|
203
|
+
headers: {
|
|
204
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
205
|
+
Accept: "application/json",
|
|
206
|
+
"User-Agent": version_ts_1.USER_AGENT,
|
|
207
|
+
},
|
|
208
|
+
body,
|
|
209
|
+
signal,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
catch (cause) {
|
|
213
|
+
throw new errors_ts_1.TransportError(`Could not reach the token endpoint at ${this.config.gatewayBaseURL}/oauth2/token.`, { cause });
|
|
214
|
+
}
|
|
215
|
+
const text = await response.text();
|
|
216
|
+
let parsed;
|
|
217
|
+
try {
|
|
218
|
+
parsed = text ? JSON.parse(text) : undefined;
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
parsed = undefined;
|
|
222
|
+
}
|
|
223
|
+
const payload = (typeof parsed === "object" && parsed !== null ? parsed : {});
|
|
224
|
+
if (!response.ok)
|
|
225
|
+
throw tokenError(response, payload, text);
|
|
226
|
+
const accessToken = payload["access_token"];
|
|
227
|
+
const expiresIn = payload["expires_in"];
|
|
228
|
+
if (typeof accessToken !== "string" || !accessToken) {
|
|
229
|
+
throw new errors_ts_1.TransportError("The token response carried no access_token.");
|
|
230
|
+
}
|
|
231
|
+
if (typeof expiresIn !== "number" || !(expiresIn > 0)) {
|
|
232
|
+
throw new errors_ts_1.TransportError(`The auth-service returned an unusable expires_in: ${JSON.stringify(expiresIn)}.`);
|
|
233
|
+
}
|
|
234
|
+
// The GRANTED scope, never the requested one. Asking for a subset is honoured, so what came back
|
|
235
|
+
// is what this token can actually do -- and a caller that assumed otherwise would diagnose a 403
|
|
236
|
+
// as a platform fault.
|
|
237
|
+
const granted = payload["scope"];
|
|
238
|
+
const scope = typeof granted === "string" ? granted.split(" ").filter(Boolean) : [];
|
|
239
|
+
const lifetime = Math.min(effectiveLifetime(response.headers, accessToken, expiresIn), MAX_PLAUSIBLE_TTL_SECONDS);
|
|
240
|
+
return {
|
|
241
|
+
accessToken,
|
|
242
|
+
expiresAt: issuedAt + lifetime * 1000,
|
|
243
|
+
expiresIn: lifetime,
|
|
244
|
+
scope,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
exports.TokenManager = TokenManager;
|
|
249
|
+
/**
|
|
250
|
+
* Types a failed token response, which arrives in either of two envelopes.
|
|
251
|
+
*
|
|
252
|
+
* A `4xx` is an RFC 6749 outcome — wrong credentials, a scope the account does not hold — and is
|
|
253
|
+
* never worth retrying. A `5xx` is the gateway failing to do its job and comes back as problem+json,
|
|
254
|
+
* so it maps into the gateway taxonomy where `upstream-unavailable` is retryable.
|
|
255
|
+
*/
|
|
256
|
+
function tokenError(response, payload, text) {
|
|
257
|
+
if (response.status >= 500) {
|
|
258
|
+
const type = typeof payload["type"] === "string" ? payload["type"] : "";
|
|
259
|
+
return (0, errors_ts_1.errorFromProblem)({
|
|
260
|
+
status: response.status,
|
|
261
|
+
typeSuffix: type.split("/").pop() ?? "",
|
|
262
|
+
title: typeof payload["title"] === "string" ? payload["title"] : "",
|
|
263
|
+
detail: typeof payload["detail"] === "string"
|
|
264
|
+
? payload["detail"]
|
|
265
|
+
: `The token endpoint answered ${response.status}.`,
|
|
266
|
+
instance: typeof payload["instance"] === "string" ? payload["instance"] : "",
|
|
267
|
+
retryAfter: undefined,
|
|
268
|
+
meta: {
|
|
269
|
+
// Body first, then the headers. Same fallback as every other error path, and load-bearing for
|
|
270
|
+
// the same reason: these envelopes carry the ids in the body and some non-gateway answers
|
|
271
|
+
// carry them only in the headers.
|
|
272
|
+
requestId: (typeof payload["request_id"] === "string" ? payload["request_id"] : undefined) ??
|
|
273
|
+
response.headers.get("X-Request-ID") ??
|
|
274
|
+
undefined,
|
|
275
|
+
traceId: (typeof payload["trace_id"] === "string" ? payload["trace_id"] : undefined) ??
|
|
276
|
+
response.headers.get("X-Trace-ID") ??
|
|
277
|
+
undefined,
|
|
278
|
+
instance: undefined,
|
|
279
|
+
instanceId: undefined,
|
|
280
|
+
idempotentReplay: false,
|
|
281
|
+
idempotentReplayOf: undefined,
|
|
282
|
+
rateLimit: undefined,
|
|
283
|
+
ignoredParameters: undefined,
|
|
284
|
+
},
|
|
285
|
+
raw: payload,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
const code = typeof payload["error"] === "string" ? payload["error"] : "";
|
|
289
|
+
if (!code) {
|
|
290
|
+
// A 4xx that is not RFC 6749 at all. Said plainly, because the usual cause is that something
|
|
291
|
+
// other than the gateway answered -- a proxy, a login page -- and the status alone misleads.
|
|
292
|
+
return new errors_ts_1.TransportError(`The token endpoint answered ${response.status} without an RFC 6749 error code. ` +
|
|
293
|
+
`Body: ${text.slice(0, 200)}`);
|
|
294
|
+
}
|
|
295
|
+
const description = typeof payload["error_description"] === "string" ? payload["error_description"] : "";
|
|
296
|
+
return (0, errors_ts_1.errorFromOAuth)(response.status, code, description);
|
|
297
|
+
}
|
|
298
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"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"}
|