Skip to content
Merged
Show file tree
Hide file tree
Changes from 250 commits
Commits
Show all changes
307 commits
Select commit Hold shift + click to select a range
7a925f9
Merge branch 'graphql-the-http-layer-is-apollos-and-expresss' into gr…
nuriyoo Oct 26, 2026
6583c21
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 26, 2026
2517eb8
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 26, 2026
8917910
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 26, 2026
2798146
fireflies: rows for a named operation, two operations' errors, a type…
nuriyoo Oct 26, 2026
ac90a8f
fireflies: the crawl holds no more than the errors it stops at, and t…
nuriyoo Oct 26, 2026
758720b
graphql: the run comment, the depth rule's docstring and a test docst…
nuriyoo Oct 26, 2026
689d0a1
Merge branch 'graphql-a-request-error-is-apollos' into linear-introsp…
nuriyoo Oct 26, 2026
48b4dbb
Merge branch 'linear-introspection-and-401-are-linears' into graphql-…
nuriyoo Oct 26, 2026
4262065
Merge branch 'graphql-the-http-layer-is-apollos-and-expresss' into gr…
nuriyoo Oct 26, 2026
34c9310
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 26, 2026
1c431cc
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 26, 2026
ef033ad
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 26, 2026
79ec35e
linear: the not-found rows sit in the body table, and the router's do…
nuriyoo Oct 26, 2026
637846b
Merge branch 'linear-introspection-and-401-are-linears' into graphql-…
nuriyoo Oct 26, 2026
c0a27ec
linear, fireflies: an empty Origin is no preflight, Fastify's plain t…
nuriyoo Oct 26, 2026
b240ac1
fireflies: a row for plain text past the limit holding a byte that is…
nuriyoo Oct 26, 2026
3ac023e
Merge branch 'graphql-the-http-layer-is-apollos-and-expresss' into gr…
nuriyoo Oct 26, 2026
a933bf3
linear, fireflies: a path is routed once its dot segments are resolve…
nuriyoo Oct 26, 2026
19cb771
linear, fireflies: control rows for a broken escape in the query besi…
nuriyoo Oct 26, 2026
f66b361
linear, fireflies: the broken escape's docstring points at the middle…
nuriyoo Oct 26, 2026
edb4a8c
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 26, 2026
0a9e4bf
docs: Linear's endpoint is each path Express hands Apollo Server once…
nuriyoo Oct 26, 2026
9327b89
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 26, 2026
8e79af0
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 26, 2026
bc8c0fa
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 26, 2026
75a1537
linear: the body table's comment wraps at 100
nuriyoo Oct 26, 2026
52d07fc
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 26, 2026
7f939e3
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 26, 2026
4177a17
linear: Express's 304 for a fresh GET, the budget's reset an hour on,…
nuriyoo Oct 26, 2026
aa97d81
linear: a row for Cloudflare's CONNECT at a path the front reads as /
nuriyoo Oct 26, 2026
8cd8e62
linear: the 304's measurement sits in _fresh's docstring
nuriyoo Oct 26, 2026
381ed77
fireflies: graphql-depth-limit's 500 carries no Cache-Control, and a …
nuriyoo Oct 26, 2026
e1faabc
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 26, 2026
a9f4909
fireflies: the budget's constants count root fields, and the docstrin…
nuriyoo Oct 26, 2026
f433004
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 26, 2026
188eac5
linear: a refused argument at __type or another nullable root field c…
nuriyoo Oct 26, 2026
fddbce1
fireflies: a refused key is held a minute from its first refusal, the…
nuriyoo Oct 26, 2026
f7c0e2f
fireflies: graphql-js's argument refusals come ahead of the budget's,…
nuriyoo Oct 26, 2026
fd1b6e7
fireflies: every resolver error comes after the budget's refusals, as…
nuriyoo Oct 26, 2026
6c8d11c
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 26, 2026
26c610e
fireflies: the hold's test table points at the docstring that dates t…
nuriyoo Oct 26, 2026
47d10a2
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 26, 2026
6451cda
linear: HEAD's 304 carries the ETag its 200 leaves off, and fresh rea…
nuriyoo Oct 26, 2026
cf451e2
linear: rows for fresh's whitespace past ASCII, and no guard the ETag…
nuriyoo Oct 26, 2026
ae434e0
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 26, 2026
2d47662
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 26, 2026
b0bedec
Merge remote-tracking branch 'origin/main' into graphql-a-request-err…
nuriyoo Oct 27, 2026
5c16437
Merge branch 'graphql-a-request-error-is-apollos' into linear-introsp…
nuriyoo Oct 27, 2026
91b6bf3
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 27, 2026
fb387cd
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 27, 2026
315f4f2
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
ed59299
Merge branch 'linear-introspection-and-401-are-linears' into graphql-…
nuriyoo Oct 27, 2026
30eb880
Merge branch 'graphql-the-http-layer-is-apollos-and-expresss' into gr…
nuriyoo Oct 27, 2026
14fea25
linear tests: a request `_LINEAR_HTTP` sends carries its headers in t…
nuriyoo Oct 27, 2026
8c66405
fireflies tests: a request `_FIREFLIES_HTTP` sends carries its header…
nuriyoo Oct 27, 2026
18230d5
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 27, 2026
6be0935
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
cc47ac0
fireflies: Spent.refuse's docstring gives the six samples of the race
nuriyoo Oct 27, 2026
4aacdf0
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
5237085
linear: a 405 and OPTIONS / with If-None-Match: * are answered as wit…
nuriyoo Oct 27, 2026
f5d6896
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 27, 2026
f668fb6
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
a0a81b5
linear: an If-None-Match tag is stripped of spaces alone, as fresh re…
nuriyoo Oct 27, 2026
56cf79a
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 27, 2026
92b8d32
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
61a14b8
fireflies: the race's sentence names its two fields, and the order te…
nuriyoo Oct 27, 2026
2b1b800
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
74a4fc1
Merge remote-tracking branch 'origin/main' into graphql-a-request-err…
nuriyoo Oct 27, 2026
852daf8
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 27, 2026
0e6a5f5
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 27, 2026
f395a93
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
58ffa2d
Merge branch 'graphql-a-request-error-is-apollos' into linear-introsp…
nuriyoo Oct 27, 2026
30e8a84
Merge branch 'linear-introspection-and-401-are-linears' into graphql-…
nuriyoo Oct 27, 2026
542c550
Merge branch 'graphql-the-http-layer-is-apollos-and-expresss' into gr…
nuriyoo Oct 27, 2026
40633d1
linear: past the limit the request bucket's level stops at 0, as Line…
nuriyoo Oct 27, 2026
98ee09e
fireflies: the review's notes on the telemetry row, HSTS, a body refu…
nuriyoo Oct 27, 2026
ea23334
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 27, 2026
f6f371f
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
e177aa4
Merge remote-tracking branch 'origin/main' into graphql-a-request-err…
nuriyoo Oct 27, 2026
b5c76e7
Merge branch 'graphql-a-request-error-is-apollos' into linear-introsp…
nuriyoo Oct 27, 2026
f7df1d5
Merge branch 'linear-introspection-and-401-are-linears' into graphql-…
nuriyoo Oct 27, 2026
d999ea3
Merge branch 'graphql-the-http-layer-is-apollos-and-expresss' into gr…
nuriyoo Oct 27, 2026
a3048df
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 27, 2026
75170e8
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 27, 2026
714444a
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
b225c74
Merge branch 'linear-introspection-and-401-are-linears' into graphql-…
nuriyoo Oct 27, 2026
f45e9d4
Merge branch 'graphql-the-http-layer-is-apollos-and-expresss' into gr…
nuriyoo Oct 27, 2026
b9fd3dd
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 27, 2026
f2ebdfe
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 27, 2026
a682c17
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
21c1af2
linear tests: _linear_status's docstring points at the table that rea…
nuriyoo Oct 27, 2026
78c3be0
tests: each measured table of the HTTP layer runs as one test
nuriyoo Oct 27, 2026
ac95809
Merge branch 'graphql-the-http-layer-is-apollos-and-expresss' into gr…
nuriyoo Oct 27, 2026
fc6c41f
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 27, 2026
fc72d46
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 27, 2026
54d520c
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
3193fc4
linear tests: the rows whose answer without a key turns on what the d…
nuriyoo Oct 27, 2026
257efc8
linear tests: each stage of Apollo Server's that refuses ahead of the…
nuriyoo Oct 27, 2026
a973fbd
Merge remote-tracking branch 'origin/main' into graphql-a-request-err…
nuriyoo Oct 27, 2026
d2ad248
Merge branch 'graphql-a-request-error-is-apollos' into linear-introsp…
nuriyoo Oct 27, 2026
8b774e8
Merge branch 'linear-introspection-and-401-are-linears' into graphql-…
nuriyoo Oct 27, 2026
ec8c95f
Merge branch 'graphql-the-http-layer-is-apollos-and-expresss' into gr…
nuriyoo Oct 27, 2026
b8553f4
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 27, 2026
a0b1789
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 27, 2026
929567f
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
73ef323
Merge remote-tracking branch 'origin/main' into graphql-a-request-err…
nuriyoo Oct 27, 2026
08a2510
Merge branch 'graphql-a-request-error-is-apollos' into linear-introsp…
nuriyoo Oct 27, 2026
90f44f4
Merge branch 'linear-introspection-and-401-are-linears' into graphql-…
nuriyoo Oct 27, 2026
a85ce77
Merge branch 'graphql-the-http-layer-is-apollos-and-expresss' into gr…
nuriyoo Oct 27, 2026
2a18028
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 27, 2026
644cd85
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 27, 2026
b498175
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
8dc2705
fireflies tests: _sent's docstring wraps at the line length
nuriyoo Oct 27, 2026
0f8a9f7
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 27, 2026
b255180
linear tests: the headers test's docstring points at the comment over…
nuriyoo Oct 27, 2026
dd02e64
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 27, 2026
52c9992
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
a0bdd4f
linear: a body declared utf-7 or utf-7-imap is read as iconv-lite rea…
nuriyoo Oct 27, 2026
1c7f720
linear: a br body is decompressed as body-parser 2.x decompresses it
nuriyoo Oct 27, 2026
dc1fc78
Merge remote-tracking branch 'origin/main' into graphql-a-request-err…
nuriyoo Oct 27, 2026
139245c
Merge remote-tracking branch 'origin/main' into graphql-a-request-err…
nuriyoo Oct 27, 2026
b347ab1
fireflies: _envelope names every code _FRIENDLY holds
nuriyoo Oct 27, 2026
86b2a47
Merge branch 'graphql-a-request-error-is-apollos' into linear-introsp…
nuriyoo Oct 27, 2026
7fe4dda
Merge branch 'linear-introspection-and-401-are-linears' into graphql-…
nuriyoo Oct 27, 2026
4624cbd
Merge remote-tracking branch 'origin/main' into graphql-a-request-err…
nuriyoo Oct 27, 2026
9dcffc2
Merge branch 'graphql-a-request-error-is-apollos' into linear-introsp…
nuriyoo Oct 27, 2026
be29416
Merge branch 'linear-introspection-and-401-are-linears' into graphql-…
nuriyoo Oct 27, 2026
098af60
linear: the Brotli read takes the decoder's one call, and the UTF-7 a…
nuriyoo Oct 27, 2026
9fcdb3c
linear: the decoders' docstrings state the measurement in the present…
nuriyoo Oct 27, 2026
5cae426
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 27, 2026
dcbf71c
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 27, 2026
9320a05
Merge branch 'graphql-the-http-layer-is-apollos-and-expresss' into gr…
nuriyoo Oct 27, 2026
9d4bd6b
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
1c2f6bb
Merge branch 'linear-a-refused-root-argument-is-answered-first' into …
nuriyoo Oct 27, 2026
1afd5aa
fireflies tests: an unknown id's answer ends with the newline each Fi…
nuriyoo Oct 27, 2026
f245bf3
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
9ea1134
Merge branch 'linear-a-refused-root-argument-is-answered-first' into …
nuriyoo Oct 27, 2026
560831b
linear: If-None-Match is read without the whitespace around it, and C…
nuriyoo Oct 27, 2026
0cbee48
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 27, 2026
e3f40ac
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
8933ddc
Merge branch 'linear-a-refused-root-argument-is-answered-first' into …
nuriyoo Oct 27, 2026
e5f3c7c
express_http: the fresh versions are named once, on NO_CACHE
nuriyoo Oct 27, 2026
7d0163d
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 27, 2026
903cd0d
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 27, 2026
658ca29
Merge branch 'linear-a-refused-root-argument-is-answered-first' into …
nuriyoo Oct 27, 2026
b5b27d0
Merge remote-tracking branch 'origin/main' into graphql-a-request-err…
nuriyoo Oct 28, 2026
326fc66
graphql: a query a persisted query's hash matches is parsed however e…
nuriyoo Oct 28, 2026
5b3917b
Merge branch 'graphql-a-request-error-is-apollos' into linear-introsp…
nuriyoo Oct 28, 2026
6aa5d5e
tests: a hidden by-id root's answer equals an absent one's, body for …
nuriyoo Oct 28, 2026
e5d1ac8
Merge branch 'linear-introspection-and-401-are-linears' into graphql-…
nuriyoo Oct 28, 2026
b11dda5
tests: Cloudflare's answers ahead of Linear and Fireflies are one tab…
nuriyoo Oct 28, 2026
8052b14
main, tests, NOTICE.md: the table comment points at the middlewares, …
nuriyoo Oct 28, 2026
ce5cc22
graphql: the module docstring names a Refusal's code and message, and…
nuriyoo Oct 28, 2026
8b7d08a
graphql: js_words names the five refusals graphql-js 17 words otherwise
nuriyoo Oct 28, 2026
22e386e
Merge branch 'graphql-a-request-error-is-apollos' into linear-introsp…
nuriyoo Oct 28, 2026
722d3ce
Merge remote-tracking branch 'origin/main' into graphql-a-request-err…
nuriyoo Oct 28, 2026
7c8e02f
Merge branch 'graphql-a-request-error-is-apollos' into linear-introsp…
nuriyoo Oct 28, 2026
270eaf3
Merge branch 'linear-introspection-and-401-are-linears' into graphql-…
nuriyoo Oct 28, 2026
4576a71
Merge branch 'graphql-the-http-layer-is-apollos-and-expresss' into gr…
nuriyoo Oct 28, 2026
a364056
linear, tests: the route docstring describes one route, and the Cloud…
nuriyoo Oct 28, 2026
c6c2d1c
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 28, 2026
fe01b35
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 28, 2026
3e60d59
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 28, 2026
bd96be1
Merge branch 'linear-a-refused-root-argument-is-answered-first' into …
nuriyoo Oct 28, 2026
cf7a758
Merge branch 'main' into graphql-a-request-error-is-apollos
nuriyoo Oct 28, 2026
4a69884
linear, docs, tests: Cloudflare's CONNECT carries nosniff at slashes …
nuriyoo Oct 28, 2026
527dc3c
tests: the headers each Linear answer carries are columns of _LINEAR_…
nuriyoo Oct 28, 2026
5f0f5ec
linear, tests: an If-None-Match keeps what follows it but spaces and …
nuriyoo Oct 28, 2026
331612b
body: iconv-lite strips a byte-order mark from what write returns, no…
nuriyoo Oct 28, 2026
19a5436
linear: a preflight's header names lose the U+00A0s around them, and …
nuriyoo Oct 28, 2026
c3a1193
linear: the fresh cases _fresh's docstring listed are rows of _LINEAR…
nuriyoo Oct 28, 2026
d4232e4
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 28, 2026
b333970
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 28, 2026
67a8a69
Merge branch 'linear-a-refused-root-argument-is-answered-first' into …
nuriyoo Oct 28, 2026
7aac439
linear: CONNECT at a path climbing past the root carries no nosniff, …
nuriyoo Oct 28, 2026
73de666
Merge branch 'linear-a-refused-root-argument-is-answered-first' into …
nuriyoo Oct 28, 2026
c317ec6
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 28, 2026
07e301c
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 28, 2026
2572044
tests: Linear's answers to a UTF-7 run of a lone high surrogate and o…
nuriyoo Oct 28, 2026
62f6351
graphql: js_words names each kind of refusal graphql-js 17 answers ot…
nuriyoo Oct 28, 2026
fbb2247
linear: introspection is answered without a credential, the 401 is Li…
nuriyoo Oct 28, 2026
919b6c8
Merge commit 'fbb2247e' into graphql-the-http-layer-is-apollos-and-ex…
nuriyoo Oct 28, 2026
8aa5790
linear, fireflies: each method, content type and Accept is answered b…
nuriyoo Oct 28, 2026
1bad7ec
Merge remote-tracking branch 'origin/main' into graphql-a-request-err…
nuriyoo Oct 28, 2026
f4d2fd9
Merge commit '919b6c8' into graphql-each-path-is-routed-as-the-vendor…
nuriyoo Oct 28, 2026
e7efff6
Merge remote-tracking branch 'origin/graphql-a-request-error-is-apoll…
nuriyoo Oct 28, 2026
3d4d8de
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 28, 2026
ed56506
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 28, 2026
82a0a76
tests: the headers each Fireflies answer carries are columns of _FIRE…
nuriyoo Oct 28, 2026
381c57e
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 28, 2026
a5c5b5f
Merge branch 'linear-a-refused-root-argument-is-answered-first' into …
nuriyoo Oct 28, 2026
9aa7464
fireflies: an alias or a fragment named __proto__ spends the budget a…
nuriyoo Oct 28, 2026
5c91435
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 28, 2026
46c7d49
Merge branch 'linear-a-refused-root-argument-is-answered-first' into …
nuriyoo Oct 28, 2026
ea30e3f
Merge remote-tracking branch 'origin/main' into graphql-each-path-is-…
nuriyoo Oct 28, 2026
be7d6df
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 28, 2026
b2c34a7
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 28, 2026
5c37c4c
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 28, 2026
454d24a
Merge branch 'linear-a-refused-root-argument-is-answered-first' into …
nuriyoo Oct 28, 2026
467a033
Merge branch 'main' into graphql-each-path-is-routed-as-the-vendor-ro…
nuriyoo Oct 28, 2026
54f6520
Merge remote-tracking branch 'origin/main' into graphql-each-path-is-…
nuriyoo Oct 29, 2026
57041cc
linear, fireflies: a cut escape, a `..` past the root and each dot se…
nuriyoo Oct 29, 2026
15bd499
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 29, 2026
457f667
linear, docs: the header notes cover 2026-10-25 to 2026-10-28, the CO…
nuriyoo Oct 29, 2026
69c10ff
linear, tests: nosniff goes on each answer at a path Linear's load ba…
nuriyoo Oct 29, 2026
6a3677c
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 29, 2026
eaa59d0
fireflies, docs: the refusal switch has its own Fireflies section in …
nuriyoo Oct 29, 2026
cd892e2
fireflies, tests: the minute's and the day's budget are settings defa…
nuriyoo Oct 29, 2026
d0dbb95
fireflies, docs: the budget's root fields are those a resolver runs f…
nuriyoo Oct 29, 2026
b883a2b
Merge remote-tracking branch 'origin/fireflies-each-answer-carries-fi…
nuriyoo Oct 29, 2026
ec6d8ce
linear, graphql: a nullable root field's refused argument is answered…
nuriyoo Oct 29, 2026
4fe3fed
linear: the row of __type's refused argument alone, where the run rea…
nuriyoo Oct 29, 2026
e4c6e40
graphql: run's docstring keeps the run's errors at the other root fie…
nuriyoo Oct 29, 2026
e6deb0c
Merge branch 'main' into graphql-each-path-is-routed-as-the-vendor-ro…
nuriyoo Oct 29, 2026
d4e3150
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 29, 2026
88c6fc8
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 29, 2026
f16b2df
Merge remote-tracking branch 'origin/main' into graphql-each-path-is-…
nuriyoo Oct 29, 2026
a1df66d
Merge branch 'linear-a-refused-root-argument-is-answered-first' into …
nuriyoo Oct 29, 2026
e7846e7
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 29, 2026
eaef523
uv.lock: written by the uv that writes main's, revision 5, so the dif…
nuriyoo Oct 29, 2026
f9e065a
_unbrotli: the comment on the prefix search says what the search does…
nuriyoo Oct 29, 2026
3e52f87
_unbrotli: a stream cut short past the limit, then bytes Brotli canno…
nuriyoo Oct 29, 2026
abf6def
test_linear: rows for what follows no-cache in Cache-Control, which f…
nuriyoo Oct 29, 2026
998147e
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 29, 2026
f39a131
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 29, 2026
6a201ee
Merge branch 'linear-a-refused-root-argument-is-answered-first' into …
nuriyoo Oct 29, 2026
aaa69ab
tests: each raw request is built by one helper beside raw_exchange
nuriyoo Oct 30, 2026
86a22aa
Merge remote-tracking branch 'origin/main' into graphql-each-path-is-…
nuriyoo Oct 30, 2026
97f4a21
Merge remote-tracking branch 'origin/graphql-each-path-is-routed-as-t…
nuriyoo Oct 30, 2026
fd3d787
Merge remote-tracking branch 'origin/linear-each-answer-carries-linea…
nuriyoo Oct 30, 2026
810b2cf
Merge remote-tracking branch 'origin/fireflies-each-answer-carries-fi…
nuriyoo Oct 30, 2026
52dc2bb
Merge remote-tracking branch 'origin/linear-a-refused-root-argument-i…
nuriyoo Oct 30, 2026
1ca0082
Merge remote-tracking branch 'origin/main' into graphql-each-path-is-…
nuriyoo Oct 30, 2026
dc88aad
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 30, 2026
12e4771
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 30, 2026
4a01294
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 30, 2026
83c8d48
Merge branch 'linear-a-refused-root-argument-is-answered-first' into …
nuriyoo Oct 30, 2026
90ecce4
Merge remote-tracking branch 'origin/main' into graphql-each-path-is-…
nuriyoo Oct 30, 2026
6a0e4f3
Merge branch 'graphql-each-path-is-routed-as-the-vendor-routes-it' in…
nuriyoo Oct 30, 2026
ab421ed
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 30, 2026
88a35cb
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 30, 2026
aedc72e
Merge branch 'linear-a-refused-root-argument-is-answered-first' into …
nuriyoo Oct 30, 2026
becd462
Merge branch 'main' into graphql-each-path-is-routed-as-the-vendor-ro…
nuriyoo Oct 30, 2026
68827e6
Merge remote-tracking branch 'origin/graphql-each-path-is-routed-as-t…
nuriyoo Oct 30, 2026
93d711c
Merge branch 'linear-each-answer-carries-linears-headers' into firefl…
nuriyoo Oct 30, 2026
9c0ba59
Merge branch 'fireflies-each-answer-carries-fireflies-headers-and-bud…
nuriyoo Oct 30, 2026
24fd32b
Merge branch 'linear-a-refused-root-argument-is-answered-first' into …
nuriyoo Oct 30, 2026
21c2b18
Merge branch 'main' into linear-a-utf-7-body-is-read-as-iconv-lite-re…
nuriyoo Oct 30, 2026
d91672d
Merge remote-tracking branch 'origin/main' into linear-a-utf-7-body-i…
nuriyoo Oct 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions backlot/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ def _resolve_path_defaults(cls, values):
# has what the switch does and when to flip it.
github_enforce_rate_limits: bool = True

# --- fireflies ---
# On by default: a mock more permissive than the vendor hides the refusal a client meets in
# production (see ``backlot.routers.fireflies.Budget``). docs/configuration.md's Fireflies
# section has what the switches do and when to flip them.
fireflies_enforce_rate_limits: bool = True
# The root fields a key may run resolvers for in a minute and in a calendar day in UTC, a Free
# plan's: the `x-ratelimit-limit-api_burst` and `x-ratelimit-limit-api` api.fireflies.ai sent
# the key measured with on 2026-10-26 and 2026-10-29 (UTC), and the "30 requests" and "500
# requests" its limits page gives the plan.
fireflies_per_minute: int = 30
fireflies_per_day: int = 500

# --- usage telemetry (backlot.telemetry; docs/telemetry.md says what is sent) ---
telemetry: bool = True
# The `DO_NOT_TRACK` convention many developer tools honour, read without the prefix, from the
Expand Down
20 changes: 20 additions & 0 deletions backlot/express_http.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""What Express does alike for each vendor whose answers it writes."""

from __future__ import annotations

import base64
import hashlib
import re


def weak_etag(body: bytes) -> str:
"""Express's weak `ETag` for ``body``, as the `etag` package writes it: the body's length in
bytes, in hex, and the first 27 characters of the base64 SHA-1 of the body."""
digest = base64.b64encode(hashlib.sha1(body).digest()).decode("ascii")[:27]
return f'W/"{len(body):x}-{digest}"'


#: ``fresh``'s test for `no-cache` among the comma-separated parts of a request's `Cache-Control`,
#: the same in 0.5.2, which Express 4 asks, and in 2.0.0, which Express 5 asks, its `\s` written
#: out as the characters JavaScript's `\s` holds in latin-1, which a header value is read as.
NO_CACHE = re.compile(r"(?:^|,)[\t\n\v\f\r \xa0]*?no-cache[\t\n\v\f\r \xa0]*?(?:,|$)")
141 changes: 130 additions & 11 deletions backlot/graphql/body.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from __future__ import annotations

import base64
import json
import json.decoder
import math
Expand All @@ -18,6 +19,8 @@
from decimal import Decimal
from typing import Any

import brotli as brotli_codec

from backlot.graphql import v8_json

# `json.decoder`'s whitespace and string scanner, and `json.scanner.NUMBER_RE` under `re.ASCII`: the
Expand Down Expand Up @@ -47,9 +50,9 @@ def decode(raw: bytes) -> str:
return text[1:] if text.startswith("\ufeff") else text


#: The most bytes Linear's ``body-parser`` reads, counted after :func:`_inflate`: api.linear.app
#: served a body of 10485760 bytes and refused one of 10485761 with :class:`Unread`'s 413 on
#: 2026-10-19 (UTC), each sent plain and gzipped.
#: The most bytes Linear's ``body-parser`` reads, counted after :func:`_inflate` or
#: :func:`_unbrotli`: api.linear.app served a body of 10485760 bytes and refused one of 10485761
#: with :class:`Unread`'s 413 on 2026-10-19 (UTC), each sent plain and gzipped.
LINEAR_LIMIT = 10 * 1024 * 1024

# The charsets iconv-lite names with `utf`, as `encodingExists` reads a name: in lower case, without
Expand Down Expand Up @@ -93,8 +96,8 @@ def body_parser_text(
``utf-`` is a 415, then an encoding other than ``identity``, ``gzip``, ``deflate`` and ``br``,
then an uncompressed body past ``limit`` the 413, which ``raw-body`` answers from
``Content-Length`` before it reads, then a ``utf-`` charset iconv-lite does not know the 415;
then :func:`_inflate` reads a compressed body, and the bytes are decoded in the charset's byte
order (:func:`_byte_order`).
then :func:`_inflate` or :func:`_unbrotli` reads a compressed body, and the bytes are decoded in
the charset's byte order (:func:`_byte_order`), or as UTF-7 (:func:`_utf7`).

api.linear.app answered each step on 2026-10-19 (UTC): ``latin1``, ``iso-8859-1``, ``utf8`` and
``utf_16le`` were the charset 415, ``x-gzip``, ``bogus`` and ``gzip, gzip`` the encoding's, a
Expand All @@ -103,8 +106,7 @@ def body_parser_text(
and ``utf-16le``, ``utf-16-le``, ``utf-16be``, ``utf-16`` and ``utf-32`` with and without a
byte-order mark, ``utf-32le``, ``GZIP``, ``deflate`` and an empty encoding were served. It
decompressed a ``br`` body, which body-parser 1.x refuses with the encoding's 415, so it runs a
2.x; such a body is read here as it arrives, and a ``utf-7`` or ``utf-7-imap`` one as UTF-8.
Without ``brotli``, ``br`` is the encoding's 415 as on a 1.x."""
2.x. Without ``brotli``, ``br`` is the encoding's 415 as on a 1.x."""
charset = _charset(content_type)
if not charset.startswith("utf-"):
raise Unread(415, f'unsupported charset "{charset.upper()}"')
Expand All @@ -118,18 +120,20 @@ def body_parser_text(
raise Unread(415, f'unsupported charset "{charset.upper()}"')
if encoding in ("gzip", "deflate"):
raw = _inflate(raw, gzip=encoding == "gzip", limit=limit)
if name in ("utf8", *_ICONV_UTF7):
elif encoding == "br":
raw = _unbrotli(raw, limit)
if name == "utf8":
return decode(raw)
if name in _ICONV_UTF7:
return _utf7(raw, imap=name == "utf7imap")
width = 2 if name.startswith("utf16") else 4
order = name[5:] or _byte_order(raw, width) # past `utf16` or `utf32`
raw = raw[: len(raw) - len(raw) % width]
if width == 2:
text = raw.decode(f"utf-16-{order}", "surrogatepass")
else:
points = struct.unpack(f"{'<' if order == 'le' else '>'}{len(raw) // 4}I", raw)
text = "".join(chr(p) if p <= 0x10FFFF else "\ufffd" for p in points)
# The units join into a JavaScript string, where a high surrogate then a low one is a pair.
text = text.encode("utf-16-le", "surrogatepass").decode("utf-16-le", "surrogatepass")
text = _js_string("".join(chr(p) if p <= 0x10FFFF else "\ufffd" for p in points))
return text[1:] if text.startswith("\ufeff") else text


Expand Down Expand Up @@ -275,6 +279,74 @@ def _byte_order(raw: bytes, width: int) -> str:
return "be" if score > 0 else "le"


_BASE64 = frozenset(b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")


def _utf7(raw: bytes, *, imap: bool) -> str:
"""``raw`` as iconv-lite 0.7's ``utf-7`` or ``utf-7-imap`` decoder reads it in one ``write``
and its ``end``, from ``encodings/utf7.js``. A ``+`` (``&`` for IMAP) opens a run of base64
characters, IMAP's also taking ``,`` as ``/``, that the first other byte ends; a ``-`` ending it
is dropped, and one right after the opening byte stands for that byte. Each run is decoded
apart, and a run the body ends in is decoded in two, its characters up to a multiple of eight
and the rest: see :func:`_base64_units`. A byte outside a run is a character when below 0x80
and U+FFFD otherwise. A byte-order mark is no character where it opens a run, the part of a run
the body ends in that ``end`` decodes, or what ``write`` returns, and is one where it opens what
``end`` returns, which iconv-lite's ``StripBOMWrapper`` leaves alone: api.linear.app answered so
on 2026-10-27 and 2026-10-28 (UTC), the second time quoting the mark of `+/v/+/w`, a run whose
two marks ``end`` decodes, as the token it stopped at. ``raw-body`` hands the decoder each chunk
as it arrives, so this is a body read in one chunk; one that arrives in pieces reads otherwise
where a piece ends inside a run."""
shift, alphabet = (0x26, _BASE64 | {0x2C}) if imap else (0x2B, _BASE64)
units: list[int] = []
in_run, start, i = False, 0, 0
while i < len(raw):
byte = raw[i]
if not in_run:
if byte == shift:
units += (b if b < 0x80 else 0xFFFD for b in raw[start:i])
in_run, start = True, i + 1
elif byte not in alphabet:
if i == start and byte == 0x2D:
units.append(shift)
else:
units += _base64_units(raw[start:i], imap)
in_run = False
if byte != 0x2D:
start = i
continue # the byte is read again outside the run
start = i + 1
i += 1
tail: list[int] = []
if not in_run:
units += (b if b < 0x80 else 0xFFFD for b in raw[start:])
else:
rest = raw[start:]
whole = len(rest) - len(rest) % 8
units += _base64_units(rest[:whole], imap)
tail = _base64_units(rest[whole:], imap)
if units[:1] == [0xFEFF]:
units = units[1:]
return _js_string("".join(map(chr, units + tail)))


def _js_string(text: str) -> str:
"""``text``, UTF-16 units each a character, as the JavaScript string they join into, where a
high surrogate then a low one is a pair."""
return text.encode("utf-16-le", "surrogatepass").decode("utf-16-le", "surrogatepass")


def _base64_units(chars: bytes, imap: bool) -> list[int]:
"""The UTF-16 units a run of base64 ``chars`` gives: Node's ``Buffer.from(…, "base64")``, which
drops a last character that makes no byte, then iconv-lite's ``utf16-be`` decoder, which drops a
last odd byte and a byte-order mark at the start of the run."""
if imap:
chars = chars.replace(b",", b"/")
chars = chars[: len(chars) - (len(chars) % 4 == 1)]
data = base64.b64decode(chars + b"=" * (-len(chars) % 4))
units = [data[i] << 8 | data[i + 1] for i in range(0, len(data) - 1, 2)]
return units[1:] if units[:1] == [0xFEFF] else units


def _inflate(raw: bytes, gzip: bool, limit: int = LINEAR_LIMIT) -> bytes:
"""``raw`` through Node's ``createGunzip`` or ``createInflate``, which ``body-parser`` pipes a
body through, and :class:`Unread` where that stream fails or yields more than ``limit``
Expand All @@ -297,6 +369,53 @@ def _inflate(raw: bytes, gzip: bool, limit: int = LINEAR_LIMIT) -> bytes:
return bytes(out)


def _unbrotli(raw: bytes, limit: int = LINEAR_LIMIT) -> bytes:
"""``raw`` through Node's ``createBrotliDecompress``, which ``body-parser`` 2.x pipes a ``br``
body through, and :class:`Unread` where that stream fails or yields more than ``limit`` bytes.
Node reads one stream and leaves what follows it unread; the messages are zlib's
``Decompression failed`` for bytes Brotli cannot read and ``unexpected end of file`` for a
stream cut short. Measured on api.linear.app on 2026-10-27 (UTC): a stream then ``xx``, or then
a second stream, was served, no bytes and a stream short of its last three were the end of
file, the query sent plain was the decompression failure, and a stream one byte past
:data:`LINEAR_LIMIT` the 413 where one of that many was served. On 2026-10-29 (UTC) a stream
cut short past :data:`LINEAR_LIMIT`, then bytes Brotli cannot read, was the decompression
failure in 14 of 16 sends over three such bodies, and the 413 in the other two."""
try:
out, finished = _brotli_read(raw, limit)
except brotli_codec.error:
# Python's decoder refuses bytes after the stream: the stream ends at the shortest prefix
# that finishes it, if any does.
lo, hi = 0, len(raw)
while lo < hi:
mid = (lo + hi) // 2
try:
out, finished = _brotli_read(raw[:mid], limit)
except brotli_codec.error:
hi = mid
continue
if finished:
hi = mid
else:
lo = mid + 1
try:
out, finished = _brotli_read(raw[:lo], limit)
except brotli_codec.error:
raise Unread(400, "Decompression failed") from None
if len(out) > limit:
raise Unread(413, "request entity too large")
if not finished:
raise Unread(400, "unexpected end of file")
return out


def _brotli_read(raw: bytes, limit: int) -> tuple[bytes, bool]:
"""What a Brotli decoder fed ``raw`` gives, stopping once it holds more than ``limit`` bytes,
and whether the stream ended; ``brotli.error`` where the bytes are not Brotli's."""
decoder = brotli_codec.Decompressor()
out = decoder.process(raw, output_buffer_limit=limit + 1)
return out, decoder.is_finished()


class Double(float):
"""A number as the double ``JSON.parse`` reads it, printed as JavaScript's ``String`` prints a
number, which is how graphql-js quotes a variable's value; graphql-core quotes a float with
Expand Down
Loading
Loading