tina4-nodejs 3.13.144 → 3.13.146
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/CLAUDE.md +2 -2
- package/package.json +1 -1
- package/packages/cli/dist/bin.js +33 -11
- package/packages/core/dist/index.js +33 -11
- package/packages/core/src/api.ts +38 -11
- package/packages/orm/dist/index.js +33 -11
- package/types/core/src/api.d.ts +12 -3
package/CLAUDE.md
CHANGED
|
@@ -13,13 +13,13 @@ Even if the skill text is not currently loaded, these are non-negotiable:
|
|
|
13
13
|
|
|
14
14
|
The full discipline lives in `.claude/skills/tina4-maintainer/SKILL.md`; this block is the always-on floor.
|
|
15
15
|
|
|
16
|
-
# CLAUDE.md - AI Developer Guide for tina4-nodejs (v3.13.
|
|
16
|
+
# CLAUDE.md - AI Developer Guide for tina4-nodejs (v3.13.146)
|
|
17
17
|
|
|
18
18
|
> This file helps AI assistants (Claude, Copilot, Cursor, etc.) understand and work on this codebase effectively.
|
|
19
19
|
|
|
20
20
|
## What This Project Is
|
|
21
21
|
|
|
22
|
-
Tina4 for Node.js/TypeScript v3.13.
|
|
22
|
+
Tina4 for Node.js/TypeScript v3.13.146 - The Intelligent Native Application 4ramework. A convention-over-configuration structural paradigm. The developer writes TypeScript; Tina4 is invisible infrastructure.
|
|
23
23
|
|
|
24
24
|
The philosophy: zero ceremony, batteries included, file system as source of truth.
|
|
25
25
|
|
package/package.json
CHANGED
package/packages/cli/dist/bin.js
CHANGED
|
@@ -43003,6 +43003,13 @@ import { randomBytes as randomBytes11 } from "node:crypto";
|
|
|
43003
43003
|
import { promises as fsp, createWriteStream } from "node:fs";
|
|
43004
43004
|
import { basename as basename7 } from "node:path";
|
|
43005
43005
|
import { pipeline } from "node:stream/promises";
|
|
43006
|
+
function keepCrossOriginHeaders(headers) {
|
|
43007
|
+
const out = {};
|
|
43008
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
43009
|
+
if (KEEP_ON_CROSS_ORIGIN.has(key.toLowerCase())) out[key] = value;
|
|
43010
|
+
}
|
|
43011
|
+
return out;
|
|
43012
|
+
}
|
|
43006
43013
|
function sameOrigin(urlA, urlB) {
|
|
43007
43014
|
try {
|
|
43008
43015
|
const a = new URL2(urlA);
|
|
@@ -43146,7 +43153,7 @@ async function* parseSseStream(lines) {
|
|
|
43146
43153
|
const trailing = emit();
|
|
43147
43154
|
if (trailing) yield trailing;
|
|
43148
43155
|
}
|
|
43149
|
-
var ApiStreamError, RETRY_STATUSES, DOWNLOAD_CHUNK_SIZE, MAX_REDIRECTS, STRIP_ON_CROSS_ORIGIN, MIME_BY_EXT, Api;
|
|
43156
|
+
var ApiStreamError, RETRY_STATUSES, DOWNLOAD_CHUNK_SIZE, MAX_REDIRECTS, STRIP_ON_CROSS_ORIGIN, KEEP_ON_CROSS_ORIGIN, MIME_BY_EXT, Api;
|
|
43150
43157
|
var init_api = __esm({
|
|
43151
43158
|
"../core/src/api.ts"() {
|
|
43152
43159
|
"use strict";
|
|
@@ -43164,6 +43171,14 @@ var init_api = __esm({
|
|
|
43164
43171
|
DOWNLOAD_CHUNK_SIZE = 64 * 1024;
|
|
43165
43172
|
MAX_REDIRECTS = 10;
|
|
43166
43173
|
STRIP_ON_CROSS_ORIGIN = /* @__PURE__ */ new Set(["authorization", "cookie"]);
|
|
43174
|
+
KEEP_ON_CROSS_ORIGIN = /* @__PURE__ */ new Set([
|
|
43175
|
+
"user-agent",
|
|
43176
|
+
"accept",
|
|
43177
|
+
"accept-encoding",
|
|
43178
|
+
"accept-language",
|
|
43179
|
+
"content-type",
|
|
43180
|
+
"content-length"
|
|
43181
|
+
]);
|
|
43167
43182
|
MIME_BY_EXT = {
|
|
43168
43183
|
txt: "text/plain",
|
|
43169
43184
|
html: "text/html",
|
|
@@ -43619,6 +43634,18 @@ var init_api = __esm({
|
|
|
43619
43634
|
}
|
|
43620
43635
|
return `${this.baseUrl}/${path10.replace(/^\/+/, "")}`;
|
|
43621
43636
|
}
|
|
43637
|
+
/**
|
|
43638
|
+
* The headers configured on the client, as they may go to `targetUrl`.
|
|
43639
|
+
*
|
|
43640
|
+
* They belong to the base origin, like the token: an absolute target on
|
|
43641
|
+
* another origin gets only the cross-origin-safe ones. A client with no
|
|
43642
|
+
* base has no origin to bind them to, and sends them to the URL each call
|
|
43643
|
+
* names.
|
|
43644
|
+
*/
|
|
43645
|
+
configuredHeadersFor(targetUrl) {
|
|
43646
|
+
const offOrigin = targetUrl !== void 0 && this.baseUrl !== "" && !sameOrigin(targetUrl, this.baseUrl);
|
|
43647
|
+
return offOrigin ? keepCrossOriginHeaders(this.headers) : this.headers;
|
|
43648
|
+
}
|
|
43622
43649
|
/**
|
|
43623
43650
|
* Build the request headers (default User-Agent + auth + cookie jar +
|
|
43624
43651
|
* extras) and serialize the body to a Buffer. Shared by every verb,
|
|
@@ -43633,7 +43660,7 @@ var init_api = __esm({
|
|
|
43633
43660
|
* clobber.
|
|
43634
43661
|
*/
|
|
43635
43662
|
buildRequest(method, contentType, body, extraHeaders, targetUrl) {
|
|
43636
|
-
const headers = { "User-Agent": `Tina4/${TINA4_VERSION}`, ...this.
|
|
43663
|
+
const headers = { "User-Agent": `Tina4/${TINA4_VERSION}`, ...this.configuredHeadersFor(targetUrl) };
|
|
43637
43664
|
const sameOriginAsBase = targetUrl === void 0 || sameOrigin(targetUrl, this.baseUrl);
|
|
43638
43665
|
if (this.authHeader && sameOriginAsBase) {
|
|
43639
43666
|
headers["Authorization"] = this.authHeader;
|
|
@@ -43735,9 +43762,9 @@ var init_api = __esm({
|
|
|
43735
43762
|
* Location, this drains the intermediate response and re-issues to the new
|
|
43736
43763
|
* URL: 301/302/303 on a non-GET/HEAD become GET (body dropped, urllib
|
|
43737
43764
|
* behaviour); 307/308 preserve method + body. When the redirect target is a
|
|
43738
|
-
* DIFFERENT origin,
|
|
43739
|
-
* bearer token
|
|
43740
|
-
* authenticate to.
|
|
43765
|
+
* DIFFERENT origin, only KEEP_ON_CROSS_ORIGIN headers are carried, so a
|
|
43766
|
+
* bearer token, session cookie or any caller-set credential header never
|
|
43767
|
+
* leaks to a host you didn't authenticate to.
|
|
43741
43768
|
*/
|
|
43742
43769
|
async performRequest(method, url, headers, data, redirectsLeft) {
|
|
43743
43770
|
try {
|
|
@@ -43781,18 +43808,13 @@ var init_api = __esm({
|
|
|
43781
43808
|
const crossOrigin = !sameOrigin(url, nextUrl);
|
|
43782
43809
|
let nextMethod = method;
|
|
43783
43810
|
let nextData = data;
|
|
43784
|
-
const nextHeaders = { ...headers };
|
|
43811
|
+
const nextHeaders = crossOrigin ? keepCrossOriginHeaders(headers) : { ...headers };
|
|
43785
43812
|
if ((status2 === 301 || status2 === 302 || status2 === 303) && method !== "GET" && method !== "HEAD") {
|
|
43786
43813
|
nextMethod = "GET";
|
|
43787
43814
|
nextData = void 0;
|
|
43788
43815
|
deleteHeaderCaseInsensitive(nextHeaders, "content-type");
|
|
43789
43816
|
deleteHeaderCaseInsensitive(nextHeaders, "content-length");
|
|
43790
43817
|
}
|
|
43791
|
-
if (crossOrigin) {
|
|
43792
|
-
for (const name of STRIP_ON_CROSS_ORIGIN) {
|
|
43793
|
-
deleteHeaderCaseInsensitive(nextHeaders, name);
|
|
43794
|
-
}
|
|
43795
|
-
}
|
|
43796
43818
|
this.performRequest(nextMethod, nextUrl, nextHeaders, nextData, redirectsLeft - 1).then(resolve29);
|
|
43797
43819
|
return;
|
|
43798
43820
|
}
|
|
@@ -42959,6 +42959,13 @@ import { randomBytes as randomBytes11 } from "node:crypto";
|
|
|
42959
42959
|
import { promises as fsp, createWriteStream } from "node:fs";
|
|
42960
42960
|
import { basename as basename6 } from "node:path";
|
|
42961
42961
|
import { pipeline } from "node:stream/promises";
|
|
42962
|
+
function keepCrossOriginHeaders(headers) {
|
|
42963
|
+
const out = {};
|
|
42964
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
42965
|
+
if (KEEP_ON_CROSS_ORIGIN.has(key.toLowerCase())) out[key] = value;
|
|
42966
|
+
}
|
|
42967
|
+
return out;
|
|
42968
|
+
}
|
|
42962
42969
|
function sameOrigin(urlA, urlB) {
|
|
42963
42970
|
try {
|
|
42964
42971
|
const a = new URL2(urlA);
|
|
@@ -43102,7 +43109,7 @@ async function* parseSseStream(lines) {
|
|
|
43102
43109
|
const trailing = emit();
|
|
43103
43110
|
if (trailing) yield trailing;
|
|
43104
43111
|
}
|
|
43105
|
-
var ApiStreamError, RETRY_STATUSES, DOWNLOAD_CHUNK_SIZE, MAX_REDIRECTS, STRIP_ON_CROSS_ORIGIN, MIME_BY_EXT, Api;
|
|
43112
|
+
var ApiStreamError, RETRY_STATUSES, DOWNLOAD_CHUNK_SIZE, MAX_REDIRECTS, STRIP_ON_CROSS_ORIGIN, KEEP_ON_CROSS_ORIGIN, MIME_BY_EXT, Api;
|
|
43106
43113
|
var init_api = __esm({
|
|
43107
43114
|
"src/api.ts"() {
|
|
43108
43115
|
"use strict";
|
|
@@ -43120,6 +43127,14 @@ var init_api = __esm({
|
|
|
43120
43127
|
DOWNLOAD_CHUNK_SIZE = 64 * 1024;
|
|
43121
43128
|
MAX_REDIRECTS = 10;
|
|
43122
43129
|
STRIP_ON_CROSS_ORIGIN = /* @__PURE__ */ new Set(["authorization", "cookie"]);
|
|
43130
|
+
KEEP_ON_CROSS_ORIGIN = /* @__PURE__ */ new Set([
|
|
43131
|
+
"user-agent",
|
|
43132
|
+
"accept",
|
|
43133
|
+
"accept-encoding",
|
|
43134
|
+
"accept-language",
|
|
43135
|
+
"content-type",
|
|
43136
|
+
"content-length"
|
|
43137
|
+
]);
|
|
43123
43138
|
MIME_BY_EXT = {
|
|
43124
43139
|
txt: "text/plain",
|
|
43125
43140
|
html: "text/html",
|
|
@@ -43575,6 +43590,18 @@ var init_api = __esm({
|
|
|
43575
43590
|
}
|
|
43576
43591
|
return `${this.baseUrl}/${path10.replace(/^\/+/, "")}`;
|
|
43577
43592
|
}
|
|
43593
|
+
/**
|
|
43594
|
+
* The headers configured on the client, as they may go to `targetUrl`.
|
|
43595
|
+
*
|
|
43596
|
+
* They belong to the base origin, like the token: an absolute target on
|
|
43597
|
+
* another origin gets only the cross-origin-safe ones. A client with no
|
|
43598
|
+
* base has no origin to bind them to, and sends them to the URL each call
|
|
43599
|
+
* names.
|
|
43600
|
+
*/
|
|
43601
|
+
configuredHeadersFor(targetUrl) {
|
|
43602
|
+
const offOrigin = targetUrl !== void 0 && this.baseUrl !== "" && !sameOrigin(targetUrl, this.baseUrl);
|
|
43603
|
+
return offOrigin ? keepCrossOriginHeaders(this.headers) : this.headers;
|
|
43604
|
+
}
|
|
43578
43605
|
/**
|
|
43579
43606
|
* Build the request headers (default User-Agent + auth + cookie jar +
|
|
43580
43607
|
* extras) and serialize the body to a Buffer. Shared by every verb,
|
|
@@ -43589,7 +43616,7 @@ var init_api = __esm({
|
|
|
43589
43616
|
* clobber.
|
|
43590
43617
|
*/
|
|
43591
43618
|
buildRequest(method, contentType, body, extraHeaders, targetUrl) {
|
|
43592
|
-
const headers = { "User-Agent": `Tina4/${TINA4_VERSION}`, ...this.
|
|
43619
|
+
const headers = { "User-Agent": `Tina4/${TINA4_VERSION}`, ...this.configuredHeadersFor(targetUrl) };
|
|
43593
43620
|
const sameOriginAsBase = targetUrl === void 0 || sameOrigin(targetUrl, this.baseUrl);
|
|
43594
43621
|
if (this.authHeader && sameOriginAsBase) {
|
|
43595
43622
|
headers["Authorization"] = this.authHeader;
|
|
@@ -43691,9 +43718,9 @@ var init_api = __esm({
|
|
|
43691
43718
|
* Location, this drains the intermediate response and re-issues to the new
|
|
43692
43719
|
* URL: 301/302/303 on a non-GET/HEAD become GET (body dropped, urllib
|
|
43693
43720
|
* behaviour); 307/308 preserve method + body. When the redirect target is a
|
|
43694
|
-
* DIFFERENT origin,
|
|
43695
|
-
* bearer token
|
|
43696
|
-
* authenticate to.
|
|
43721
|
+
* DIFFERENT origin, only KEEP_ON_CROSS_ORIGIN headers are carried, so a
|
|
43722
|
+
* bearer token, session cookie or any caller-set credential header never
|
|
43723
|
+
* leaks to a host you didn't authenticate to.
|
|
43697
43724
|
*/
|
|
43698
43725
|
async performRequest(method, url, headers, data, redirectsLeft) {
|
|
43699
43726
|
try {
|
|
@@ -43737,18 +43764,13 @@ var init_api = __esm({
|
|
|
43737
43764
|
const crossOrigin = !sameOrigin(url, nextUrl);
|
|
43738
43765
|
let nextMethod = method;
|
|
43739
43766
|
let nextData = data;
|
|
43740
|
-
const nextHeaders = { ...headers };
|
|
43767
|
+
const nextHeaders = crossOrigin ? keepCrossOriginHeaders(headers) : { ...headers };
|
|
43741
43768
|
if ((status2 === 301 || status2 === 302 || status2 === 303) && method !== "GET" && method !== "HEAD") {
|
|
43742
43769
|
nextMethod = "GET";
|
|
43743
43770
|
nextData = void 0;
|
|
43744
43771
|
deleteHeaderCaseInsensitive(nextHeaders, "content-type");
|
|
43745
43772
|
deleteHeaderCaseInsensitive(nextHeaders, "content-length");
|
|
43746
43773
|
}
|
|
43747
|
-
if (crossOrigin) {
|
|
43748
|
-
for (const name of STRIP_ON_CROSS_ORIGIN) {
|
|
43749
|
-
deleteHeaderCaseInsensitive(nextHeaders, name);
|
|
43750
|
-
}
|
|
43751
|
-
}
|
|
43752
43774
|
this.performRequest(nextMethod, nextUrl, nextHeaders, nextData, redirectsLeft - 1).then(resolve21);
|
|
43753
43775
|
return;
|
|
43754
43776
|
}
|
package/packages/core/src/api.ts
CHANGED
|
@@ -153,6 +153,26 @@ const MAX_REDIRECTS = 10;
|
|
|
153
153
|
*/
|
|
154
154
|
const STRIP_ON_CROSS_ORIGIN: ReadonlySet<string> = new Set(["authorization", "cookie"]);
|
|
155
155
|
|
|
156
|
+
/**
|
|
157
|
+
* The only headers carried onto a different origin. A caller-set header can
|
|
158
|
+
* hold a credential under any name (X-Api-Key, X-Auth-Token, ...), so a
|
|
159
|
+
* denylist cannot contain it: the headers configured on the client (and any
|
|
160
|
+
* per-call header, on a redirect) are bound to the origin they were meant for,
|
|
161
|
+
* and only these content-negotiation headers cross. Lower-case.
|
|
162
|
+
*/
|
|
163
|
+
const KEEP_ON_CROSS_ORIGIN: ReadonlySet<string> = new Set([
|
|
164
|
+
"user-agent", "accept", "accept-encoding", "accept-language", "content-type", "content-length",
|
|
165
|
+
]);
|
|
166
|
+
|
|
167
|
+
/** Only the headers allowed onto a different origin (KEEP_ON_CROSS_ORIGIN). */
|
|
168
|
+
function keepCrossOriginHeaders(headers: Record<string, string>): Record<string, string> {
|
|
169
|
+
const out: Record<string, string> = {};
|
|
170
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
171
|
+
if (KEEP_ON_CROSS_ORIGIN.has(key.toLowerCase())) out[key] = value;
|
|
172
|
+
}
|
|
173
|
+
return out;
|
|
174
|
+
}
|
|
175
|
+
|
|
156
176
|
/**
|
|
157
177
|
* Minimal extension → MIME map for guessing a multipart part's Content-Type.
|
|
158
178
|
* Kept in-repo so the client stays zero-dependency (Node has no stdlib
|
|
@@ -909,6 +929,19 @@ export class Api {
|
|
|
909
929
|
return `${this.baseUrl}/${path.replace(/^\/+/, "")}`;
|
|
910
930
|
}
|
|
911
931
|
|
|
932
|
+
/**
|
|
933
|
+
* The headers configured on the client, as they may go to `targetUrl`.
|
|
934
|
+
*
|
|
935
|
+
* They belong to the base origin, like the token: an absolute target on
|
|
936
|
+
* another origin gets only the cross-origin-safe ones. A client with no
|
|
937
|
+
* base has no origin to bind them to, and sends them to the URL each call
|
|
938
|
+
* names.
|
|
939
|
+
*/
|
|
940
|
+
private configuredHeadersFor(targetUrl?: string): Record<string, string> {
|
|
941
|
+
const offOrigin = targetUrl !== undefined && this.baseUrl !== "" && !sameOrigin(targetUrl, this.baseUrl);
|
|
942
|
+
return offOrigin ? keepCrossOriginHeaders(this.headers) : this.headers;
|
|
943
|
+
}
|
|
944
|
+
|
|
912
945
|
/**
|
|
913
946
|
* Build the request headers (default User-Agent + auth + cookie jar +
|
|
914
947
|
* extras) and serialize the body to a Buffer. Shared by every verb,
|
|
@@ -929,7 +962,7 @@ export class Api {
|
|
|
929
962
|
extraHeaders?: Record<string, string>,
|
|
930
963
|
targetUrl?: string,
|
|
931
964
|
): { headers: Record<string, string>; data: Buffer | undefined } {
|
|
932
|
-
const headers: Record<string, string> = { "User-Agent": `Tina4/${TINA4_VERSION}`, ...this.
|
|
965
|
+
const headers: Record<string, string> = { "User-Agent": `Tina4/${TINA4_VERSION}`, ...this.configuredHeadersFor(targetUrl) };
|
|
933
966
|
// Attach the configured Authorization / Cookie ONLY when the request
|
|
934
967
|
// target is same-origin as the configured base. A path that is itself
|
|
935
968
|
// an absolute off-origin URL (e.g. get("http://evil/x")) otherwise
|
|
@@ -1066,9 +1099,9 @@ export class Api {
|
|
|
1066
1099
|
* Location, this drains the intermediate response and re-issues to the new
|
|
1067
1100
|
* URL: 301/302/303 on a non-GET/HEAD become GET (body dropped, urllib
|
|
1068
1101
|
* behaviour); 307/308 preserve method + body. When the redirect target is a
|
|
1069
|
-
* DIFFERENT origin,
|
|
1070
|
-
* bearer token
|
|
1071
|
-
* authenticate to.
|
|
1102
|
+
* DIFFERENT origin, only KEEP_ON_CROSS_ORIGIN headers are carried, so a
|
|
1103
|
+
* bearer token, session cookie or any caller-set credential header never
|
|
1104
|
+
* leaks to a host you didn't authenticate to.
|
|
1072
1105
|
*/
|
|
1073
1106
|
private async performRequest(
|
|
1074
1107
|
method: string,
|
|
@@ -1128,7 +1161,7 @@ export class Api {
|
|
|
1128
1161
|
const crossOrigin = !sameOrigin(url, nextUrl);
|
|
1129
1162
|
let nextMethod = method;
|
|
1130
1163
|
let nextData = data;
|
|
1131
|
-
const nextHeaders: Record<string, string> = { ...headers };
|
|
1164
|
+
const nextHeaders: Record<string, string> = crossOrigin ? keepCrossOriginHeaders(headers) : { ...headers };
|
|
1132
1165
|
|
|
1133
1166
|
// 301/302/303 on a body-bearing method → GET, drop the body
|
|
1134
1167
|
// (matches urllib's HTTPRedirectHandler); 307/308 preserve.
|
|
@@ -1143,12 +1176,6 @@ export class Api {
|
|
|
1143
1176
|
deleteHeaderCaseInsensitive(nextHeaders, "content-length");
|
|
1144
1177
|
}
|
|
1145
1178
|
|
|
1146
|
-
if (crossOrigin) {
|
|
1147
|
-
for (const name of STRIP_ON_CROSS_ORIGIN) {
|
|
1148
|
-
deleteHeaderCaseInsensitive(nextHeaders, name);
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
1179
|
this.performRequest(nextMethod, nextUrl, nextHeaders, nextData, redirectsLeft - 1).then(resolve);
|
|
1153
1180
|
return;
|
|
1154
1181
|
}
|
|
@@ -31466,6 +31466,13 @@ import { randomBytes as randomBytes9 } from "node:crypto";
|
|
|
31466
31466
|
import { promises as fsp, createWriteStream } from "node:fs";
|
|
31467
31467
|
import { basename as basename6 } from "node:path";
|
|
31468
31468
|
import { pipeline } from "node:stream/promises";
|
|
31469
|
+
function keepCrossOriginHeaders(headers) {
|
|
31470
|
+
const out = {};
|
|
31471
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
31472
|
+
if (KEEP_ON_CROSS_ORIGIN.has(key.toLowerCase())) out[key] = value;
|
|
31473
|
+
}
|
|
31474
|
+
return out;
|
|
31475
|
+
}
|
|
31469
31476
|
function sameOrigin(urlA, urlB) {
|
|
31470
31477
|
try {
|
|
31471
31478
|
const a = new URL2(urlA);
|
|
@@ -31609,7 +31616,7 @@ async function* parseSseStream(lines) {
|
|
|
31609
31616
|
const trailing = emit();
|
|
31610
31617
|
if (trailing) yield trailing;
|
|
31611
31618
|
}
|
|
31612
|
-
var ApiStreamError, RETRY_STATUSES, DOWNLOAD_CHUNK_SIZE, MAX_REDIRECTS, STRIP_ON_CROSS_ORIGIN, MIME_BY_EXT, Api;
|
|
31619
|
+
var ApiStreamError, RETRY_STATUSES, DOWNLOAD_CHUNK_SIZE, MAX_REDIRECTS, STRIP_ON_CROSS_ORIGIN, KEEP_ON_CROSS_ORIGIN, MIME_BY_EXT, Api;
|
|
31613
31620
|
var init_api = __esm({
|
|
31614
31621
|
"../core/src/api.ts"() {
|
|
31615
31622
|
"use strict";
|
|
@@ -31627,6 +31634,14 @@ var init_api = __esm({
|
|
|
31627
31634
|
DOWNLOAD_CHUNK_SIZE = 64 * 1024;
|
|
31628
31635
|
MAX_REDIRECTS = 10;
|
|
31629
31636
|
STRIP_ON_CROSS_ORIGIN = /* @__PURE__ */ new Set(["authorization", "cookie"]);
|
|
31637
|
+
KEEP_ON_CROSS_ORIGIN = /* @__PURE__ */ new Set([
|
|
31638
|
+
"user-agent",
|
|
31639
|
+
"accept",
|
|
31640
|
+
"accept-encoding",
|
|
31641
|
+
"accept-language",
|
|
31642
|
+
"content-type",
|
|
31643
|
+
"content-length"
|
|
31644
|
+
]);
|
|
31630
31645
|
MIME_BY_EXT = {
|
|
31631
31646
|
txt: "text/plain",
|
|
31632
31647
|
html: "text/html",
|
|
@@ -32082,6 +32097,18 @@ var init_api = __esm({
|
|
|
32082
32097
|
}
|
|
32083
32098
|
return `${this.baseUrl}/${path10.replace(/^\/+/, "")}`;
|
|
32084
32099
|
}
|
|
32100
|
+
/**
|
|
32101
|
+
* The headers configured on the client, as they may go to `targetUrl`.
|
|
32102
|
+
*
|
|
32103
|
+
* They belong to the base origin, like the token: an absolute target on
|
|
32104
|
+
* another origin gets only the cross-origin-safe ones. A client with no
|
|
32105
|
+
* base has no origin to bind them to, and sends them to the URL each call
|
|
32106
|
+
* names.
|
|
32107
|
+
*/
|
|
32108
|
+
configuredHeadersFor(targetUrl) {
|
|
32109
|
+
const offOrigin = targetUrl !== void 0 && this.baseUrl !== "" && !sameOrigin(targetUrl, this.baseUrl);
|
|
32110
|
+
return offOrigin ? keepCrossOriginHeaders(this.headers) : this.headers;
|
|
32111
|
+
}
|
|
32085
32112
|
/**
|
|
32086
32113
|
* Build the request headers (default User-Agent + auth + cookie jar +
|
|
32087
32114
|
* extras) and serialize the body to a Buffer. Shared by every verb,
|
|
@@ -32096,7 +32123,7 @@ var init_api = __esm({
|
|
|
32096
32123
|
* clobber.
|
|
32097
32124
|
*/
|
|
32098
32125
|
buildRequest(method, contentType, body, extraHeaders, targetUrl) {
|
|
32099
|
-
const headers = { "User-Agent": `Tina4/${TINA4_VERSION}`, ...this.
|
|
32126
|
+
const headers = { "User-Agent": `Tina4/${TINA4_VERSION}`, ...this.configuredHeadersFor(targetUrl) };
|
|
32100
32127
|
const sameOriginAsBase = targetUrl === void 0 || sameOrigin(targetUrl, this.baseUrl);
|
|
32101
32128
|
if (this.authHeader && sameOriginAsBase) {
|
|
32102
32129
|
headers["Authorization"] = this.authHeader;
|
|
@@ -32198,9 +32225,9 @@ var init_api = __esm({
|
|
|
32198
32225
|
* Location, this drains the intermediate response and re-issues to the new
|
|
32199
32226
|
* URL: 301/302/303 on a non-GET/HEAD become GET (body dropped, urllib
|
|
32200
32227
|
* behaviour); 307/308 preserve method + body. When the redirect target is a
|
|
32201
|
-
* DIFFERENT origin,
|
|
32202
|
-
* bearer token
|
|
32203
|
-
* authenticate to.
|
|
32228
|
+
* DIFFERENT origin, only KEEP_ON_CROSS_ORIGIN headers are carried, so a
|
|
32229
|
+
* bearer token, session cookie or any caller-set credential header never
|
|
32230
|
+
* leaks to a host you didn't authenticate to.
|
|
32204
32231
|
*/
|
|
32205
32232
|
async performRequest(method, url, headers, data, redirectsLeft) {
|
|
32206
32233
|
try {
|
|
@@ -32244,18 +32271,13 @@ var init_api = __esm({
|
|
|
32244
32271
|
const crossOrigin = !sameOrigin(url, nextUrl);
|
|
32245
32272
|
let nextMethod = method;
|
|
32246
32273
|
let nextData = data;
|
|
32247
|
-
const nextHeaders = { ...headers };
|
|
32274
|
+
const nextHeaders = crossOrigin ? keepCrossOriginHeaders(headers) : { ...headers };
|
|
32248
32275
|
if ((status2 === 301 || status2 === 302 || status2 === 303) && method !== "GET" && method !== "HEAD") {
|
|
32249
32276
|
nextMethod = "GET";
|
|
32250
32277
|
nextData = void 0;
|
|
32251
32278
|
deleteHeaderCaseInsensitive(nextHeaders, "content-type");
|
|
32252
32279
|
deleteHeaderCaseInsensitive(nextHeaders, "content-length");
|
|
32253
32280
|
}
|
|
32254
|
-
if (crossOrigin) {
|
|
32255
|
-
for (const name of STRIP_ON_CROSS_ORIGIN) {
|
|
32256
|
-
deleteHeaderCaseInsensitive(nextHeaders, name);
|
|
32257
|
-
}
|
|
32258
|
-
}
|
|
32259
32281
|
this.performRequest(nextMethod, nextUrl, nextHeaders, nextData, redirectsLeft - 1).then(resolve21);
|
|
32260
32282
|
return;
|
|
32261
32283
|
}
|
package/types/core/src/api.d.ts
CHANGED
|
@@ -318,6 +318,15 @@ export declare class Api {
|
|
|
318
318
|
*/
|
|
319
319
|
private openStreamRequest;
|
|
320
320
|
private buildUrl;
|
|
321
|
+
/**
|
|
322
|
+
* The headers configured on the client, as they may go to `targetUrl`.
|
|
323
|
+
*
|
|
324
|
+
* They belong to the base origin, like the token: an absolute target on
|
|
325
|
+
* another origin gets only the cross-origin-safe ones. A client with no
|
|
326
|
+
* base has no origin to bind them to, and sends them to the URL each call
|
|
327
|
+
* names.
|
|
328
|
+
*/
|
|
329
|
+
private configuredHeadersFor;
|
|
321
330
|
/**
|
|
322
331
|
* Build the request headers (default User-Agent + auth + cookie jar +
|
|
323
332
|
* extras) and serialize the body to a Buffer. Shared by every verb,
|
|
@@ -357,9 +366,9 @@ export declare class Api {
|
|
|
357
366
|
* Location, this drains the intermediate response and re-issues to the new
|
|
358
367
|
* URL: 301/302/303 on a non-GET/HEAD become GET (body dropped, urllib
|
|
359
368
|
* behaviour); 307/308 preserve method + body. When the redirect target is a
|
|
360
|
-
* DIFFERENT origin,
|
|
361
|
-
* bearer token
|
|
362
|
-
* authenticate to.
|
|
369
|
+
* DIFFERENT origin, only KEEP_ON_CROSS_ORIGIN headers are carried, so a
|
|
370
|
+
* bearer token, session cookie or any caller-set credential header never
|
|
371
|
+
* leaks to a host you didn't authenticate to.
|
|
363
372
|
*/
|
|
364
373
|
private performRequest;
|
|
365
374
|
/** Buffer a response body, parse JSON if possible, and store cookies. */
|