stitchkit 0.90.4 → 0.90.6
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/CHANGELOG.md +102 -0
- package/README.md +2 -0
- package/dist/agent-runtime-coding-tools.js +5 -4
- package/dist/agent-runtime-harness.js +6 -5
- package/dist/agent-runtime-sandbox.js +5 -4
- package/dist/agent-runtime.js +9 -8
- package/dist/application/grammy.d.ts.map +1 -1
- package/dist/application-grammy.js +4 -1
- package/dist/cli.js +62 -18
- package/dist/contract/index.js +2 -1
- package/dist/google.d.ts +56 -0
- package/dist/google.d.ts.map +1 -0
- package/dist/google.js +175 -0
- package/dist/{index-32vjke6q.js → index-1bnkzq95.js} +1 -1
- package/dist/{index-db51n3xr.js → index-640tz39q.js} +5 -5
- package/dist/{index-19ryv24q.js → index-83fafqw8.js} +3 -3
- package/dist/{index-z78bjcxj.js → index-aj9geeez.js} +1 -1
- package/dist/{index-bg8ypxzn.js → index-bva395we.js} +6 -7
- package/dist/{index-y47z614h.js → index-fqg5mfk7.js} +145 -32
- package/dist/index-hr4tzbwq.js +11 -0
- package/dist/{index-kazec06k.js → index-nrqg8tks.js} +5 -19
- package/dist/index-rxfy4cq7.js +17 -0
- package/dist/{index-6atvfjc1.js → index-s208wab5.js} +1 -1
- package/dist/{index-7t0wq6j5.js → index-tgh3ksfh.js} +5 -5
- package/dist/{index-6q3zjwaz.js → index-vy2nxwg8.js} +1 -1
- package/dist/{index-kzxpsf8y.js → index-zsdgd1tz.js} +1 -1
- package/dist/index.js +10 -9
- package/dist/internal/pkce.d.ts +3 -0
- package/dist/internal/pkce.d.ts.map +1 -0
- package/dist/node.js +5 -4
- package/dist/oauth.d.ts +57 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +170 -0
- package/dist/observability/index.js +3 -2
- package/dist/primitives.js +2 -1
- package/dist/remote.js +4 -3
- package/dist/server/bun.d.ts.map +1 -1
- package/dist/server/index.js +14 -11
- package/dist/server/middleware/pkce.d.ts +1 -2
- package/dist/server/middleware/pkce.d.ts.map +1 -1
- package/dist/testing.js +5 -4
- package/dist/tool-invoker.js +7 -6
- package/dist/tools/cli-args.d.ts +10 -8
- package/dist/tools/cli-args.d.ts.map +1 -1
- package/dist/tools/cli-installer.d.ts +7 -2
- package/dist/tools/cli-installer.d.ts.map +1 -1
- package/dist/tools/cli-view.d.ts.map +1 -1
- package/dist/tools/cli.d.ts.map +1 -1
- package/dist/tools/connections/index.js +37 -4
- package/dist/tools/connections/mcp-envelope.d.ts +44 -0
- package/dist/tools/connections/mcp-envelope.d.ts.map +1 -0
- package/dist/tools/connections/mcp.d.ts.map +1 -1
- package/dist/tools.js +16 -14
- package/dist/tracking.js +3 -2
- package/llms-full.txt +224 -8
- package/llms.txt +1 -0
- package/package.json +36 -22
- package/dist/{index-8crm1srv.js → index-3yqgj323.js} +3 -3
- package/dist/{index-vcnfwrtr.js → index-8pjqv3zh.js} +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,108 @@ additive**; the first breaking change landed in 0.10.0. Grep the file for
|
|
|
15
15
|
|
|
16
16
|
## [Unreleased]
|
|
17
17
|
|
|
18
|
+
## [0.90.6] — 2026-09-20
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **Browser Authorization Code + PKCE transactions in `stitchkit/oauth`.** The new
|
|
23
|
+
browser-safe entrypoint creates authorization URLs, stores one-time state and
|
|
24
|
+
verifier records, and consumes callbacks with bounded expiry, replay protection,
|
|
25
|
+
safe return paths and provider errors that do not expose credentials.
|
|
26
|
+
- **A Google OpenID Connect server adapter in `stitchkit/google`.** It exchanges an
|
|
27
|
+
authorization code through the official optional `google-auth-library` peer,
|
|
28
|
+
requires an exact redirect allowlist, validates issuer, audience, expiry and nonce,
|
|
29
|
+
and returns a small stable identity without leaking tokens into application state.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **Unix-socket servers no longer forward Bun's TCP-only `http2` option.** The Unix
|
|
34
|
+
variant now strips it beside the other transport-specific settings.
|
|
35
|
+
- **grammY startup accepts the native application abort signal on its Node typing
|
|
36
|
+
boundary.** Runtime cancellation was already compatible; the adapter now makes that
|
|
37
|
+
compatibility explicit instead of failing consumer type-checks.
|
|
38
|
+
- **The lane temporary-directory test is isolated from concurrent lanes.** Its real
|
|
39
|
+
default-path proof runs in a child process with a private system temp root, removing
|
|
40
|
+
the full-suite timeout caused by scanning unrelated live descriptors.
|
|
41
|
+
- **Concurrent packed lanes serialize the shared core build.** Two release lanes can no
|
|
42
|
+
longer clean `dist` underneath each other's `prepack`, which previously produced a
|
|
43
|
+
JavaScript-only tarball during a parallel full gate.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- **Supported dependencies and consumer fixtures track current stable releases.** The
|
|
48
|
+
MCP Apps peer accepts both the existing 1.x line and current 2.x, while development
|
|
49
|
+
and optional-peer lanes exercise 2.x; the `srvx` peer likewise keeps its supported
|
|
50
|
+
0.12 line while adding 1.x.
|
|
51
|
+
|
|
52
|
+
## [0.90.5] — 2026-09-16
|
|
53
|
+
|
|
54
|
+
Three gaps a consuming project found on the first live surface after adopting
|
|
55
|
+
0.90.4 — each one a place where the framework handed over parts of the job and
|
|
56
|
+
kept the composition for itself.
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- **A connection-mounted command on the CLI prints the answer, not the MCP
|
|
61
|
+
envelope.** `tools/call` returns `{ content: [...], structuredContent? }`, and
|
|
62
|
+
an agent mount needs exactly that — the parts are what a model is shown. The
|
|
63
|
+
CLI is different in kind, because the handler's value is what gets printed,
|
|
64
|
+
piped and aggregated. Handed the envelope, the first real command answered
|
|
65
|
+
`no record carries the field "status" — available: text, type`: it was
|
|
66
|
+
grouping the content parts. `--json` had the same defect one layer down, with
|
|
67
|
+
the answer a JSON *string* inside `content[0].text`, so every consumer
|
|
68
|
+
unwrapped before anything else worked. The CLI transport now unwraps, and only
|
|
69
|
+
it: `structuredContent` when the server sent one, a lone text part when it
|
|
70
|
+
parses as JSON, its text when it does not. Several parts, an image or audio
|
|
71
|
+
pass through whole — picking one of many would be inventing an answer.
|
|
72
|
+
|
|
73
|
+
- **A remote tool's refusal keeps its code and details.** `mountConnections` ended
|
|
74
|
+
a failed `tools/call` by throwing a one-sentence `Error` and discarding the
|
|
75
|
+
result. Three things went with it: the code, so `exitCodes` had nothing to map
|
|
76
|
+
and "not found" and "no rights" both exited `1` — the per-class exit codes the
|
|
77
|
+
CLI surface exists to provide could not work over a connection at all; the
|
|
78
|
+
message the operator needed; and the error's own class, because a plain `Error`
|
|
79
|
+
is an *unexpected* error to the runner, which printed a code frame of the
|
|
80
|
+
minified framework bundle before the JSON failure and then scrubbed the whole
|
|
81
|
+
thing to `INTERNAL_SERVER_ERROR`. A structured `{ error, details }` body is now
|
|
82
|
+
relayed as the contract error it is, on every transport — losing a code is
|
|
83
|
+
missing information, not a presentation choice. A refusal without one fails as
|
|
84
|
+
`UPSTREAM_TOOL_ERROR` carrying what the server did send; the status is 502
|
|
85
|
+
either way, because whatever the code says the failure happened upstream.
|
|
86
|
+
|
|
87
|
+
### Added
|
|
88
|
+
|
|
89
|
+
- **`--sort <field>` and `--ascending` — the n largest *records*.** 0.90.4 gave
|
|
90
|
+
`--top` to groups and left "the five biggest, as a table" with no expression,
|
|
91
|
+
because `--table` took neither `--by` nor `--top`. Two readings of `--top`
|
|
92
|
+
would have been exactly the ambiguity that release removed from `--by`, so
|
|
93
|
+
ordering gets its own word: **`--by` groups, `--sort` orders**, and `--top`
|
|
94
|
+
keeps one meaning throughout — the n leading entries of the view that was
|
|
95
|
+
asked for. `--table` and `--sort` describe the same view and therefore
|
|
96
|
+
compose: `--top 5 --sort messages --table id,messages`. `--sort` without
|
|
97
|
+
`--table` returns the ordered records as JSON. A record carrying no value for
|
|
98
|
+
the sort field stays **last in both directions**: it is not the smallest, it
|
|
99
|
+
is not on the scale. `--sort` together with `--by`, `--count-by` or `--sum` is
|
|
100
|
+
refused rather than given a second meaning.
|
|
101
|
+
- **`--help <substring>` — a narrower question than "all of them".** On a
|
|
102
|
+
discovered surface `--help` is the only way to learn what exists, and that can
|
|
103
|
+
be two hundred commands and 230 lines: at that size the list stops being an
|
|
104
|
+
answer, scrolling past a person and costing an agent the same context an
|
|
105
|
+
unfiltered result would. The filter matches a command's name *or* its
|
|
106
|
+
description — the word someone knows is often in the sentence rather than the
|
|
107
|
+
name — and the heading says how many of the total matched. No match exits with
|
|
108
|
+
whatever `NOT_FOUND` maps to rather than succeeding over an empty list, because
|
|
109
|
+
`0` there reads as "there are none", a different statement from "none of
|
|
110
|
+
these". `-h <text>`, `help <text>` and `--help=<text>` are the same question;
|
|
111
|
+
bare `--help` is byte-for-byte unchanged, and `--help=false` keeps its meaning
|
|
112
|
+
as the reserved boolean's negation.
|
|
113
|
+
- **`renderCliInstaller` renders every published target in one script.** Omit
|
|
114
|
+
`asset` and the script selects by `uname` at run time, mapping `x86_64` →
|
|
115
|
+
`x64` and `aarch64` → `arm64` itself, and naming an unpublished combination
|
|
116
|
+
with what *is* published beside it. Passing one `asset` still pins one target.
|
|
117
|
+
That dispatch was the last hand-written piece of the install path, and it is
|
|
118
|
+
the same table every publisher writes from memory. It still parses no JSON.
|
|
119
|
+
|
|
18
120
|
## [0.90.4] — 2026-09-16
|
|
19
121
|
|
|
20
122
|
### Added
|
package/README.md
CHANGED
|
@@ -78,6 +78,7 @@ import { createSocketIOClient } from 'stitchkit'
|
|
|
78
78
|
import { createCursorQuery, createCacheBridge } from 'stitchkit/react'
|
|
79
79
|
import { AgentRunSchema, AgentRuntimeEventSchema } from 'stitchkit/agent-runtime/browser'
|
|
80
80
|
import { parseSSE } from 'stitchkit'
|
|
81
|
+
import { createAuthorizationCodeClient } from 'stitchkit/oauth'
|
|
81
82
|
```
|
|
82
83
|
|
|
83
84
|
Server code imports server entrypoints:
|
|
@@ -90,6 +91,7 @@ import { createAgentRuntime, defineAgentProtocol } from 'stitchkit/agent-runtime
|
|
|
90
91
|
import { createBunSqliteAgentRuntimeStore } from 'stitchkit/agent-runtime/sqlite/bun'
|
|
91
92
|
import { createApplication, defineManagedResource } from 'stitchkit/application'
|
|
92
93
|
import { implementRemote } from 'stitchkit/remote'
|
|
94
|
+
import { createGoogleOidcClient } from 'stitchkit/google'
|
|
93
95
|
```
|
|
94
96
|
|
|
95
97
|
The root `stitchkit` entrypoint is browser-safe. Server, tool, optional managed
|
|
@@ -3,16 +3,17 @@ import {
|
|
|
3
3
|
AgentCodingToolLimitsSchema,
|
|
4
4
|
AgentCodingToolPathAuthorizationSchema,
|
|
5
5
|
createAgentCodingTools
|
|
6
|
-
} from "./index-
|
|
6
|
+
} from "./index-3yqgj323.js";
|
|
7
7
|
import"./index-bn2cjajt.js";
|
|
8
8
|
import"./index-gte38nbm.js";
|
|
9
|
-
import"./index-
|
|
9
|
+
import"./index-nrqg8tks.js";
|
|
10
|
+
import"./index-rxfy4cq7.js";
|
|
10
11
|
import"./index-6k1937bx.js";
|
|
11
|
-
import"./index-scs3f1eg.js";
|
|
12
|
-
import"./index-kzfs85xp.js";
|
|
13
12
|
import"./index-8z9we758.js";
|
|
14
13
|
import"./index-fenaekmk.js";
|
|
15
14
|
import"./index-cby4ar3v.js";
|
|
15
|
+
import"./index-scs3f1eg.js";
|
|
16
|
+
import"./index-kzfs85xp.js";
|
|
16
17
|
import"./index-77fekveh.js";
|
|
17
18
|
export {
|
|
18
19
|
createAgentCodingTools,
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
composeAgentPrompt,
|
|
7
7
|
createAgentRuntime,
|
|
8
8
|
createAgentRuntimeEventSink
|
|
9
|
-
} from "./index-
|
|
9
|
+
} from "./index-s208wab5.js";
|
|
10
10
|
import"./index-p13mwz16.js";
|
|
11
11
|
import"./index-y64k3s0h.js";
|
|
12
12
|
import {
|
|
@@ -19,21 +19,22 @@ import {
|
|
|
19
19
|
walkContainedFiles
|
|
20
20
|
} from "./index-bn2cjajt.js";
|
|
21
21
|
import"./index-7rkhw9ec.js";
|
|
22
|
-
import"./index-
|
|
22
|
+
import"./index-8pjqv3zh.js";
|
|
23
23
|
import"./index-vsbzgd7b.js";
|
|
24
24
|
import"./index-f9mb610r.js";
|
|
25
25
|
import"./index-2hryh65w.js";
|
|
26
|
-
import"./index-
|
|
26
|
+
import"./index-nrqg8tks.js";
|
|
27
|
+
import"./index-rxfy4cq7.js";
|
|
27
28
|
import"./index-6k1937bx.js";
|
|
28
|
-
import"./index-scs3f1eg.js";
|
|
29
29
|
import"./index-zcgf3gqf.js";
|
|
30
30
|
import"./index-7zbps32p.js";
|
|
31
|
-
import"./index-kzfs85xp.js";
|
|
32
31
|
import {
|
|
33
32
|
defineRuntimeTool
|
|
34
33
|
} from "./index-8z9we758.js";
|
|
35
34
|
import"./index-fenaekmk.js";
|
|
36
35
|
import"./index-cby4ar3v.js";
|
|
36
|
+
import"./index-scs3f1eg.js";
|
|
37
|
+
import"./index-kzfs85xp.js";
|
|
37
38
|
import"./index-77fekveh.js";
|
|
38
39
|
|
|
39
40
|
// src/agent-runtime/harness-contract.ts
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createAgentCodingTools
|
|
3
|
-
} from "./index-
|
|
3
|
+
} from "./index-3yqgj323.js";
|
|
4
4
|
import"./index-bn2cjajt.js";
|
|
5
5
|
import {
|
|
6
6
|
codingRefusal,
|
|
7
7
|
missingSandboxRestrictions,
|
|
8
8
|
probeAgentProcessSandbox
|
|
9
9
|
} from "./index-gte38nbm.js";
|
|
10
|
-
import"./index-
|
|
10
|
+
import"./index-nrqg8tks.js";
|
|
11
|
+
import"./index-rxfy4cq7.js";
|
|
11
12
|
import"./index-6k1937bx.js";
|
|
12
|
-
import"./index-scs3f1eg.js";
|
|
13
|
-
import"./index-kzfs85xp.js";
|
|
14
13
|
import"./index-8z9we758.js";
|
|
15
14
|
import"./index-fenaekmk.js";
|
|
16
15
|
import"./index-cby4ar3v.js";
|
|
16
|
+
import"./index-scs3f1eg.js";
|
|
17
|
+
import"./index-kzfs85xp.js";
|
|
17
18
|
import"./index-77fekveh.js";
|
|
18
19
|
|
|
19
20
|
// src/agent-runtime/sandbox-bubblewrap.ts
|
package/dist/agent-runtime.js
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
renderAgentStateSlots,
|
|
32
32
|
repairedSearchCall,
|
|
33
33
|
selectAgentHistory
|
|
34
|
-
} from "./index-
|
|
34
|
+
} from "./index-s208wab5.js";
|
|
35
35
|
import"./index-p13mwz16.js";
|
|
36
36
|
import {
|
|
37
37
|
AgentAdmissionEventSchema,
|
|
@@ -169,28 +169,29 @@ import {
|
|
|
169
169
|
import {
|
|
170
170
|
buildToolManifest,
|
|
171
171
|
mountAgent
|
|
172
|
-
} from "./index-
|
|
172
|
+
} from "./index-83fafqw8.js";
|
|
173
173
|
import"./index-7rkhw9ec.js";
|
|
174
|
-
import"./index-
|
|
175
|
-
import"./index-
|
|
176
|
-
import"./index-
|
|
174
|
+
import"./index-zsdgd1tz.js";
|
|
175
|
+
import"./index-1bnkzq95.js";
|
|
176
|
+
import"./index-8pjqv3zh.js";
|
|
177
177
|
import"./index-vsbzgd7b.js";
|
|
178
178
|
import"./index-f9mb610r.js";
|
|
179
179
|
import"./index-2hryh65w.js";
|
|
180
|
-
import"./index-
|
|
180
|
+
import"./index-nrqg8tks.js";
|
|
181
|
+
import"./index-rxfy4cq7.js";
|
|
181
182
|
import"./index-6k1937bx.js";
|
|
182
|
-
import"./index-scs3f1eg.js";
|
|
183
183
|
import {
|
|
184
184
|
childSpan,
|
|
185
185
|
createTraceContext
|
|
186
186
|
} from "./index-zcgf3gqf.js";
|
|
187
187
|
import"./index-7zbps32p.js";
|
|
188
|
-
import"./index-kzfs85xp.js";
|
|
189
188
|
import {
|
|
190
189
|
defineRuntimeTool
|
|
191
190
|
} from "./index-8z9we758.js";
|
|
192
191
|
import"./index-fenaekmk.js";
|
|
193
192
|
import"./index-cby4ar3v.js";
|
|
193
|
+
import"./index-scs3f1eg.js";
|
|
194
|
+
import"./index-kzfs85xp.js";
|
|
194
195
|
import {
|
|
195
196
|
executableAgentRuntimeTools
|
|
196
197
|
} from "./index-77fekveh.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grammy.d.ts","sourceRoot":"","sources":["../../src/application/grammy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAC3F,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAMzE,MAAM,WAAW,2BAA2B,CAAC,CAAC,SAAS,OAAO;IAC5D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;IACnD,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAcD,iEAAiE;AACjE,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,OAAO,EACrD,MAAM,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACrC,eAAe,CAiEjB;AAED,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE9F,MAAM,WAAW,2BAA2B,CAAC,CAAC,SAAS,OAAO;IAC5D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,qBAAqB,CAAC,CAAC,SAAS,OAAO;IACtD,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,YAAY,CACV,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,EACvB,oBAAoB,CAAC,EAAE,oBAAoB,GAC1C,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"grammy.d.ts","sourceRoot":"","sources":["../../src/application/grammy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAC3F,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAMzE,MAAM,WAAW,2BAA2B,CAAC,CAAC,SAAS,OAAO;IAC5D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;IACnD,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAcD,iEAAiE;AACjE,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,OAAO,EACrD,MAAM,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACrC,eAAe,CAiEjB;AAED,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE9F,MAAM,WAAW,2BAA2B,CAAC,CAAC,SAAS,OAAO;IAC5D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,qBAAqB,CAAC,CAAC,SAAS,OAAO;IACtD,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,YAAY,CACV,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,EACvB,oBAAoB,CAAC,EAAE,oBAAoB,GAC1C,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB;AAaD;;;;;;;;;;;GAWG;AACH,qBAAa,6BAA8B,SAAQ,QAAQ,CAAC,8BAA8B,CAAC;IACzF,cAOC;CACF;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,SAAS,OAAO,EAC3D,MAAM,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACrC,qBAAqB,CAAC,CAAC,CAAC,CAiE1B"}
|
|
@@ -86,6 +86,9 @@ function grammyPollingResource(config) {
|
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
|
+
function grammyNodeSignal(signal) {
|
|
90
|
+
return signal;
|
|
91
|
+
}
|
|
89
92
|
|
|
90
93
|
class GrammyWebhookUnavailableError extends AppError {
|
|
91
94
|
constructor() {
|
|
@@ -115,7 +118,7 @@ function createGrammyWebhookResource(config) {
|
|
|
115
118
|
...config.dependsOn && { dependsOn: config.dependsOn },
|
|
116
119
|
...config.required !== undefined && { required: config.required },
|
|
117
120
|
async start(context) {
|
|
118
|
-
await config.bot.init(context.signal);
|
|
121
|
+
await config.bot.init(grammyNodeSignal(context.signal));
|
|
119
122
|
},
|
|
120
123
|
activate() {
|
|
121
124
|
accepting = true;
|
package/dist/cli.js
CHANGED
|
@@ -11,29 +11,30 @@ import {
|
|
|
11
11
|
readCapped,
|
|
12
12
|
renderCliView,
|
|
13
13
|
routeCliArgv
|
|
14
|
-
} from "./index-
|
|
14
|
+
} from "./index-fqg5mfk7.js";
|
|
15
15
|
import"./index-sbdmyz75.js";
|
|
16
|
-
import"./index-
|
|
17
|
-
import"./index-
|
|
18
|
-
import"./index-
|
|
16
|
+
import"./index-zsdgd1tz.js";
|
|
17
|
+
import"./index-1bnkzq95.js";
|
|
18
|
+
import"./index-8pjqv3zh.js";
|
|
19
19
|
import"./index-vsbzgd7b.js";
|
|
20
20
|
import"./index-f9mb610r.js";
|
|
21
21
|
import"./index-2hryh65w.js";
|
|
22
|
-
import"./index-
|
|
22
|
+
import"./index-nrqg8tks.js";
|
|
23
|
+
import"./index-rxfy4cq7.js";
|
|
23
24
|
import"./index-6k1937bx.js";
|
|
24
|
-
import {
|
|
25
|
-
AppError
|
|
26
|
-
} from "./index-scs3f1eg.js";
|
|
27
25
|
import"./index-zcgf3gqf.js";
|
|
28
26
|
import {
|
|
29
27
|
coerceJsonArgs
|
|
30
28
|
} from "./index-7zbps32p.js";
|
|
31
|
-
import {
|
|
32
|
-
safeJsonParse
|
|
33
|
-
} from "./index-kzfs85xp.js";
|
|
34
29
|
import"./index-8z9we758.js";
|
|
35
30
|
import"./index-fenaekmk.js";
|
|
36
31
|
import"./index-cby4ar3v.js";
|
|
32
|
+
import {
|
|
33
|
+
AppError
|
|
34
|
+
} from "./index-scs3f1eg.js";
|
|
35
|
+
import {
|
|
36
|
+
safeJsonParse
|
|
37
|
+
} from "./index-kzfs85xp.js";
|
|
37
38
|
import"./index-77fekveh.js";
|
|
38
39
|
// src/tools/cli-installer.ts
|
|
39
40
|
function shellQuote(value) {
|
|
@@ -42,20 +43,21 @@ function shellQuote(value) {
|
|
|
42
43
|
function renderCliInstaller(config) {
|
|
43
44
|
const binary = config.binaryName ?? config.manifest.name;
|
|
44
45
|
const installDir = config.installDir ?? "$HOME/.local/bin";
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
const assets = config.asset ? [config.asset] : config.manifest.assets;
|
|
47
|
+
if (assets.length === 0) {
|
|
48
|
+
throw new Error("[stitchkit] a build manifest with no assets installs nothing");
|
|
49
|
+
}
|
|
50
|
+
const selection = assets.length === 1 && assets[0] !== undefined ? singleTarget(assets[0]) : unameDispatch(assets);
|
|
48
51
|
return `#!/bin/sh
|
|
49
52
|
# ${binary} ${config.manifest.version} (${config.manifest.commit})
|
|
50
53
|
# Generated from the build manifest — it parses no JSON and needs no jq.
|
|
51
54
|
set -eu
|
|
52
55
|
|
|
53
|
-
url=${shellQuote(config.asset.url)}
|
|
54
|
-
sha256=${shellQuote(config.asset.sha256)}
|
|
55
|
-
size=${shellQuote(String(config.asset.size))}
|
|
56
56
|
binary=${shellQuote(binary)}
|
|
57
57
|
dir="\${INSTALL_DIR:-${installDir}}"
|
|
58
58
|
|
|
59
|
+
${selection}
|
|
60
|
+
|
|
59
61
|
fetch() {
|
|
60
62
|
if command -v curl >/dev/null 2>&1; then
|
|
61
63
|
curl -fsSL "$1" -o "$2"
|
|
@@ -84,7 +86,12 @@ tmp="$(mktemp -d)"
|
|
|
84
86
|
trap 'rm -rf "$tmp"' EXIT
|
|
85
87
|
|
|
86
88
|
fetch "$url" "$tmp/download"
|
|
87
|
-
$
|
|
89
|
+
if [ "$compression" = "gzip" ]; then
|
|
90
|
+
gzip -dc "$tmp/download" > "$tmp/binary"
|
|
91
|
+
else
|
|
92
|
+
mv "$tmp/download" "$tmp/binary"
|
|
93
|
+
fi
|
|
94
|
+
|
|
88
95
|
actual="$(digest "$tmp/binary")"
|
|
89
96
|
if [ "$actual" != "$sha256" ]; then
|
|
90
97
|
echo "checksum mismatch: expected $sha256, got $actual" >&2
|
|
@@ -110,6 +117,43 @@ case ":$PATH:" in
|
|
|
110
117
|
esac
|
|
111
118
|
`;
|
|
112
119
|
}
|
|
120
|
+
function singleTarget(asset) {
|
|
121
|
+
return [
|
|
122
|
+
`url=${shellQuote(asset.url)}`,
|
|
123
|
+
`sha256=${shellQuote(asset.sha256)}`,
|
|
124
|
+
`size=${shellQuote(String(asset.size))}`,
|
|
125
|
+
`compression=${shellQuote(asset.compression)}`
|
|
126
|
+
].join(`
|
|
127
|
+
`);
|
|
128
|
+
}
|
|
129
|
+
function unameDispatch(assets) {
|
|
130
|
+
const cases = assets.map((asset) => ` ${asset.platform}/${asset.arch})
|
|
131
|
+
url=${shellQuote(asset.url)}
|
|
132
|
+
sha256=${shellQuote(asset.sha256)}
|
|
133
|
+
size=${shellQuote(String(asset.size))}
|
|
134
|
+
compression=${shellQuote(asset.compression)}
|
|
135
|
+
;;`).join(`
|
|
136
|
+
`);
|
|
137
|
+
const published = assets.map((asset) => `${asset.platform}/${asset.arch}`).join(", ");
|
|
138
|
+
return `case "$(uname -s)" in
|
|
139
|
+
Darwin) platform=darwin ;;
|
|
140
|
+
Linux) platform=linux ;;
|
|
141
|
+
*) platform="$(uname -s)" ;;
|
|
142
|
+
esac
|
|
143
|
+
case "$(uname -m)" in
|
|
144
|
+
x86_64|amd64) arch=x64 ;;
|
|
145
|
+
aarch64|arm64) arch=arm64 ;;
|
|
146
|
+
*) arch="$(uname -m)" ;;
|
|
147
|
+
esac
|
|
148
|
+
|
|
149
|
+
case "$platform/$arch" in
|
|
150
|
+
${cases}
|
|
151
|
+
*)
|
|
152
|
+
echo "no published build for $platform/$arch — published: ${published}" >&2
|
|
153
|
+
exit 1
|
|
154
|
+
;;
|
|
155
|
+
esac`;
|
|
156
|
+
}
|
|
113
157
|
// src/tools/cli-manifest.ts
|
|
114
158
|
import { z } from "zod";
|
|
115
159
|
var CliBuildTargetSchema = z.object({
|
package/dist/contract/index.js
CHANGED
package/dist/google.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type GoogleOidcErrorCode = 'MISCONFIGURED' | 'INVALID_CREDENTIAL' | 'UPSTREAM_UNAVAILABLE';
|
|
2
|
+
export declare class GoogleOidcError extends Error {
|
|
3
|
+
readonly code: GoogleOidcErrorCode;
|
|
4
|
+
readonly retryable: boolean;
|
|
5
|
+
constructor(code: GoogleOidcErrorCode, options?: ErrorOptions);
|
|
6
|
+
}
|
|
7
|
+
export interface GoogleOidcIdentity {
|
|
8
|
+
subject: string;
|
|
9
|
+
email: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
picture?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface GoogleOidcClaims {
|
|
14
|
+
sub?: string;
|
|
15
|
+
email?: string;
|
|
16
|
+
emailVerified?: boolean;
|
|
17
|
+
nonce?: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
picture?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface GoogleOidcExchangeInput {
|
|
22
|
+
code: string;
|
|
23
|
+
codeVerifier: string;
|
|
24
|
+
redirectUri: string;
|
|
25
|
+
clientId: string;
|
|
26
|
+
clientSecret: string;
|
|
27
|
+
tokenEndpoint: string;
|
|
28
|
+
signal: AbortSignal;
|
|
29
|
+
}
|
|
30
|
+
export type GoogleOidcTokenTransport = (input: GoogleOidcExchangeInput) => Promise<{
|
|
31
|
+
idToken: string;
|
|
32
|
+
}>;
|
|
33
|
+
export type GoogleOidcIdTokenVerifier = (input: {
|
|
34
|
+
idToken: string;
|
|
35
|
+
audience: string;
|
|
36
|
+
}) => Promise<GoogleOidcClaims>;
|
|
37
|
+
export interface GoogleOidcClientConfig {
|
|
38
|
+
clientId: string;
|
|
39
|
+
clientSecret: string;
|
|
40
|
+
allowedRedirectUris: readonly string[];
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
fetch?: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
43
|
+
tokenTransport?: GoogleOidcTokenTransport;
|
|
44
|
+
idTokenVerifier?: GoogleOidcIdTokenVerifier;
|
|
45
|
+
}
|
|
46
|
+
export interface ExchangeGoogleAuthorizationCodeInput {
|
|
47
|
+
code: string;
|
|
48
|
+
codeVerifier: string;
|
|
49
|
+
redirectUri: string;
|
|
50
|
+
nonce: string;
|
|
51
|
+
}
|
|
52
|
+
export interface GoogleOidcClient {
|
|
53
|
+
exchangeAuthorizationCode(input: ExchangeGoogleAuthorizationCodeInput): Promise<GoogleOidcIdentity>;
|
|
54
|
+
}
|
|
55
|
+
export declare function createGoogleOidcClient(config: GoogleOidcClientConfig): GoogleOidcClient;
|
|
56
|
+
//# sourceMappingURL=google.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../src/google.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,mBAAmB,GAC3B,eAAe,GACf,oBAAoB,GACpB,sBAAsB,CAAC;AAQ3B,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAE5B,YAAY,IAAI,EAAE,mBAAmB,EAAE,OAAO,CAAC,EAAE,YAAY,EAK5D;CACF;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,MAAM,wBAAwB,GAAG,CACrC,KAAK,EAAE,uBAAuB,KAC3B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAElC,MAAM,MAAM,yBAAyB,GAAG,CAAC,KAAK,EAAE;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAEhC,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjF,cAAc,CAAC,EAAE,wBAAwB,CAAC;IAC1C,eAAe,CAAC,EAAE,yBAAyB,CAAC;CAC7C;AAED,MAAM,WAAW,oCAAoC;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,yBAAyB,CACvB,KAAK,EAAE,oCAAoC,GAC1C,OAAO,CAAC,kBAAkB,CAAC,CAAC;CAChC;AAqGD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB,GAAG,gBAAgB,CAmFvF"}
|
package/dist/google.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// src/google.ts
|
|
2
|
+
import { OAuth2Client } from "google-auth-library";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
var DEFAULT_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token";
|
|
5
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
6
|
+
var ERROR_MESSAGES = {
|
|
7
|
+
MISCONFIGURED: "Google OIDC is not configured correctly.",
|
|
8
|
+
INVALID_CREDENTIAL: "The Google credential is invalid.",
|
|
9
|
+
UPSTREAM_UNAVAILABLE: "Google identity verification is temporarily unavailable."
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
class GoogleOidcError extends Error {
|
|
13
|
+
code;
|
|
14
|
+
retryable;
|
|
15
|
+
constructor(code, options) {
|
|
16
|
+
super(ERROR_MESSAGES[code], options);
|
|
17
|
+
this.name = "GoogleOidcError";
|
|
18
|
+
this.code = code;
|
|
19
|
+
this.retryable = code === "UPSTREAM_UNAVAILABLE";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function recordOf(value) {
|
|
23
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? Object.fromEntries(Object.entries(value)) : null;
|
|
24
|
+
}
|
|
25
|
+
function statusOf(error) {
|
|
26
|
+
const record = recordOf(error);
|
|
27
|
+
const response = recordOf(record?.response);
|
|
28
|
+
return typeof response?.status === "number" ? response.status : null;
|
|
29
|
+
}
|
|
30
|
+
function codeOf(error) {
|
|
31
|
+
const record = recordOf(error);
|
|
32
|
+
return typeof record?.code === "string" ? record.code : null;
|
|
33
|
+
}
|
|
34
|
+
function upstreamVerifierFailure(error) {
|
|
35
|
+
const status = statusOf(error);
|
|
36
|
+
if (status !== null && (status === 429 || status >= 500))
|
|
37
|
+
return true;
|
|
38
|
+
return ["ECONNRESET", "ECONNREFUSED", "ETIMEDOUT", "ENOTFOUND", "EAI_AGAIN"].includes(codeOf(error) ?? "");
|
|
39
|
+
}
|
|
40
|
+
function constantTimeEqual(left, right) {
|
|
41
|
+
const leftBytes = new TextEncoder().encode(left);
|
|
42
|
+
const rightBytes = new TextEncoder().encode(right);
|
|
43
|
+
if (leftBytes.length !== rightBytes.length)
|
|
44
|
+
return false;
|
|
45
|
+
let difference = 0;
|
|
46
|
+
for (let index = 0;index < leftBytes.length; index += 1) {
|
|
47
|
+
difference |= (leftBytes[index] ?? 0) ^ (rightBytes[index] ?? 0);
|
|
48
|
+
}
|
|
49
|
+
return difference === 0;
|
|
50
|
+
}
|
|
51
|
+
function defaultTokenTransport(fetcher) {
|
|
52
|
+
return async (input) => {
|
|
53
|
+
let response;
|
|
54
|
+
try {
|
|
55
|
+
response = await fetcher(input.tokenEndpoint, {
|
|
56
|
+
method: "POST",
|
|
57
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
58
|
+
body: new URLSearchParams({
|
|
59
|
+
grant_type: "authorization_code",
|
|
60
|
+
code: input.code,
|
|
61
|
+
code_verifier: input.codeVerifier,
|
|
62
|
+
redirect_uri: input.redirectUri,
|
|
63
|
+
client_id: input.clientId,
|
|
64
|
+
client_secret: input.clientSecret
|
|
65
|
+
}),
|
|
66
|
+
signal: input.signal
|
|
67
|
+
});
|
|
68
|
+
} catch {
|
|
69
|
+
throw new GoogleOidcError("UPSTREAM_UNAVAILABLE");
|
|
70
|
+
}
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
throw new GoogleOidcError(response.status === 429 || response.status >= 500 ? "UPSTREAM_UNAVAILABLE" : "INVALID_CREDENTIAL");
|
|
73
|
+
}
|
|
74
|
+
let body;
|
|
75
|
+
try {
|
|
76
|
+
body = await response.json();
|
|
77
|
+
} catch {
|
|
78
|
+
throw new GoogleOidcError("UPSTREAM_UNAVAILABLE");
|
|
79
|
+
}
|
|
80
|
+
const idToken = recordOf(body)?.id_token;
|
|
81
|
+
if (typeof idToken !== "string" || !idToken) {
|
|
82
|
+
throw new GoogleOidcError("INVALID_CREDENTIAL");
|
|
83
|
+
}
|
|
84
|
+
return { idToken };
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function defaultIdTokenVerifier(clientId, clientSecret) {
|
|
88
|
+
const client = new OAuth2Client(clientId, clientSecret);
|
|
89
|
+
return async ({ idToken, audience }) => {
|
|
90
|
+
const ticket = await client.verifyIdToken({ idToken, audience });
|
|
91
|
+
const payload = ticket.getPayload();
|
|
92
|
+
if (!payload)
|
|
93
|
+
throw new GoogleOidcError("INVALID_CREDENTIAL");
|
|
94
|
+
return {
|
|
95
|
+
sub: payload.sub,
|
|
96
|
+
email: payload.email,
|
|
97
|
+
emailVerified: payload.email_verified,
|
|
98
|
+
nonce: payload.nonce,
|
|
99
|
+
name: payload.name,
|
|
100
|
+
picture: payload.picture
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function createGoogleOidcClient(config) {
|
|
105
|
+
const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
106
|
+
if (!config.clientId || !config.clientSecret || config.allowedRedirectUris.length === 0 || config.allowedRedirectUris.some((uri) => !uri) || !Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
107
|
+
throw new GoogleOidcError("MISCONFIGURED");
|
|
108
|
+
}
|
|
109
|
+
const allowedRedirectUris = new Set(config.allowedRedirectUris);
|
|
110
|
+
const transport = config.tokenTransport ?? defaultTokenTransport(config.fetch ?? globalThis.fetch);
|
|
111
|
+
const verify = config.idTokenVerifier ?? defaultIdTokenVerifier(config.clientId, config.clientSecret);
|
|
112
|
+
return {
|
|
113
|
+
async exchangeAuthorizationCode(input) {
|
|
114
|
+
if (!allowedRedirectUris.has(input.redirectUri)) {
|
|
115
|
+
throw new GoogleOidcError("INVALID_CREDENTIAL");
|
|
116
|
+
}
|
|
117
|
+
if (!input.code || !input.codeVerifier || !input.nonce) {
|
|
118
|
+
throw new GoogleOidcError("INVALID_CREDENTIAL");
|
|
119
|
+
}
|
|
120
|
+
const controller = new AbortController;
|
|
121
|
+
let timer;
|
|
122
|
+
const deadline = new Promise((_, reject) => {
|
|
123
|
+
timer = setTimeout(() => {
|
|
124
|
+
controller.abort();
|
|
125
|
+
reject(new GoogleOidcError("UPSTREAM_UNAVAILABLE"));
|
|
126
|
+
}, timeoutMs);
|
|
127
|
+
});
|
|
128
|
+
let exchanged;
|
|
129
|
+
try {
|
|
130
|
+
exchanged = await Promise.race([
|
|
131
|
+
transport({
|
|
132
|
+
code: input.code,
|
|
133
|
+
codeVerifier: input.codeVerifier,
|
|
134
|
+
redirectUri: input.redirectUri,
|
|
135
|
+
clientId: config.clientId,
|
|
136
|
+
clientSecret: config.clientSecret,
|
|
137
|
+
tokenEndpoint: DEFAULT_TOKEN_ENDPOINT,
|
|
138
|
+
signal: controller.signal
|
|
139
|
+
}),
|
|
140
|
+
deadline
|
|
141
|
+
]);
|
|
142
|
+
} catch (cause) {
|
|
143
|
+
if (cause instanceof GoogleOidcError)
|
|
144
|
+
throw cause;
|
|
145
|
+
throw new GoogleOidcError("UPSTREAM_UNAVAILABLE");
|
|
146
|
+
} finally {
|
|
147
|
+
if (timer)
|
|
148
|
+
clearTimeout(timer);
|
|
149
|
+
}
|
|
150
|
+
if (!exchanged.idToken)
|
|
151
|
+
throw new GoogleOidcError("INVALID_CREDENTIAL");
|
|
152
|
+
let claims;
|
|
153
|
+
try {
|
|
154
|
+
claims = await verify({ idToken: exchanged.idToken, audience: config.clientId });
|
|
155
|
+
} catch (cause) {
|
|
156
|
+
if (cause instanceof GoogleOidcError)
|
|
157
|
+
throw cause;
|
|
158
|
+
throw new GoogleOidcError(upstreamVerifierFailure(cause) ? "UPSTREAM_UNAVAILABLE" : "INVALID_CREDENTIAL");
|
|
159
|
+
}
|
|
160
|
+
if (!claims.sub || !claims.email || claims.emailVerified !== true || !claims.nonce || !constantTimeEqual(claims.nonce, input.nonce) || !z.email().safeParse(claims.email).success) {
|
|
161
|
+
throw new GoogleOidcError("INVALID_CREDENTIAL");
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
subject: claims.sub,
|
|
165
|
+
email: claims.email,
|
|
166
|
+
...claims.name ? { name: claims.name } : {},
|
|
167
|
+
...claims.picture ? { picture: claims.picture } : {}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
export {
|
|
173
|
+
createGoogleOidcClient,
|
|
174
|
+
GoogleOidcError
|
|
175
|
+
};
|