@volter/world-core 3.0.19 → 3.0.21
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/dist/generated/pack-facts.json +131 -7
- package/dist/inject.cjs +77 -1
- package/generated/pack-facts.json +131 -7
- package/inject.cjs +77 -1
- package/package.json +1 -1
|
@@ -1137,6 +1137,48 @@
|
|
|
1137
1137
|
},
|
|
1138
1138
|
"serveExport": "createGroqTwinServer"
|
|
1139
1139
|
},
|
|
1140
|
+
"hackernews": {
|
|
1141
|
+
"adoption": {
|
|
1142
|
+
"sdks": [],
|
|
1143
|
+
"envStems": [
|
|
1144
|
+
"HN",
|
|
1145
|
+
"HACKERNEWS"
|
|
1146
|
+
]
|
|
1147
|
+
},
|
|
1148
|
+
"hosts": [
|
|
1149
|
+
{
|
|
1150
|
+
"host": "hacker-news.firebaseio.com"
|
|
1151
|
+
},
|
|
1152
|
+
{
|
|
1153
|
+
"host": "news.ycombinator.com"
|
|
1154
|
+
}
|
|
1155
|
+
],
|
|
1156
|
+
"archetype": "crud",
|
|
1157
|
+
"protocol": {
|
|
1158
|
+
"declared": "3",
|
|
1159
|
+
"major": 3,
|
|
1160
|
+
"standing": "current"
|
|
1161
|
+
},
|
|
1162
|
+
"transport": "rest",
|
|
1163
|
+
"resources": [
|
|
1164
|
+
"item",
|
|
1165
|
+
"user",
|
|
1166
|
+
"_vote",
|
|
1167
|
+
"_person",
|
|
1168
|
+
"_web_session",
|
|
1169
|
+
"_web_session_issue"
|
|
1170
|
+
],
|
|
1171
|
+
"endpointEnvNone": "Hacker News publishes no SDK: clients fetch https://hacker-news.firebaseio.com/v0 directly and people use news.ycombinator.com in a browser, both reached by host",
|
|
1172
|
+
"credentialDoor": {
|
|
1173
|
+
"path": "/_twin/app-credentials",
|
|
1174
|
+
"body": {},
|
|
1175
|
+
"fill": {
|
|
1176
|
+
"HN_USERNAME": "acct",
|
|
1177
|
+
"HN_PASSWORD": "pw"
|
|
1178
|
+
}
|
|
1179
|
+
},
|
|
1180
|
+
"serveExport": "createHackernewsTwinServer"
|
|
1181
|
+
},
|
|
1140
1182
|
"hubspot": {
|
|
1141
1183
|
"adoption": {
|
|
1142
1184
|
"sdks": [
|
|
@@ -1844,6 +1886,88 @@
|
|
|
1844
1886
|
},
|
|
1845
1887
|
"serveExport": "createPosthogTwinServer"
|
|
1846
1888
|
},
|
|
1889
|
+
"reddit": {
|
|
1890
|
+
"adoption": {
|
|
1891
|
+
"sdks": [
|
|
1892
|
+
"snoowrap"
|
|
1893
|
+
],
|
|
1894
|
+
"pypi": [
|
|
1895
|
+
"praw",
|
|
1896
|
+
"asyncpraw"
|
|
1897
|
+
],
|
|
1898
|
+
"envStems": [
|
|
1899
|
+
"REDDIT"
|
|
1900
|
+
]
|
|
1901
|
+
},
|
|
1902
|
+
"hosts": [
|
|
1903
|
+
{
|
|
1904
|
+
"host": "oauth.reddit.com"
|
|
1905
|
+
},
|
|
1906
|
+
{
|
|
1907
|
+
"host": "www.reddit.com"
|
|
1908
|
+
},
|
|
1909
|
+
{
|
|
1910
|
+
"host": "reddit.com"
|
|
1911
|
+
},
|
|
1912
|
+
{
|
|
1913
|
+
"host": "reddit-uploaded-media.s3-accelerate.amazonaws.com"
|
|
1914
|
+
},
|
|
1915
|
+
{
|
|
1916
|
+
"host": "reddit-uploaded-video.s3-accelerate.amazonaws.com"
|
|
1917
|
+
},
|
|
1918
|
+
{
|
|
1919
|
+
"host": "v.redd.it"
|
|
1920
|
+
},
|
|
1921
|
+
{
|
|
1922
|
+
"host": "i.redd.it"
|
|
1923
|
+
},
|
|
1924
|
+
{
|
|
1925
|
+
"host": "preview.redd.it"
|
|
1926
|
+
},
|
|
1927
|
+
{
|
|
1928
|
+
"host": "external-preview.redd.it"
|
|
1929
|
+
},
|
|
1930
|
+
{
|
|
1931
|
+
"host": "b.thumbs.redditmedia.com"
|
|
1932
|
+
}
|
|
1933
|
+
],
|
|
1934
|
+
"archetype": "crud",
|
|
1935
|
+
"protocol": {
|
|
1936
|
+
"declared": "3",
|
|
1937
|
+
"major": 3,
|
|
1938
|
+
"standing": "current"
|
|
1939
|
+
},
|
|
1940
|
+
"transport": "rest",
|
|
1941
|
+
"resourcesUnreachable": {},
|
|
1942
|
+
"resources": [
|
|
1943
|
+
"account",
|
|
1944
|
+
"subreddit",
|
|
1945
|
+
"link",
|
|
1946
|
+
"comment",
|
|
1947
|
+
"asset",
|
|
1948
|
+
"app",
|
|
1949
|
+
"_access_token",
|
|
1950
|
+
"_token_issue",
|
|
1951
|
+
"_client_secret",
|
|
1952
|
+
"_rate_window",
|
|
1953
|
+
"_vote",
|
|
1954
|
+
"_membership",
|
|
1955
|
+
"_person",
|
|
1956
|
+
"_web_session",
|
|
1957
|
+
"_web_session_issue"
|
|
1958
|
+
],
|
|
1959
|
+
"endpointEnvNone": "Reddit's clients name oauth.reddit.com and www.reddit.com themselves (the campaign's sender writes them as constants); the World's injector routes those hosts, and no client of the demand reads a base-URL variable",
|
|
1960
|
+
"credentialDoor": {
|
|
1961
|
+
"path": "/_twin/app-credentials",
|
|
1962
|
+
"body": {
|
|
1963
|
+
"username": "volter"
|
|
1964
|
+
},
|
|
1965
|
+
"fill": {
|
|
1966
|
+
"REDDIT_ACCESS_TOKEN": "access_token"
|
|
1967
|
+
}
|
|
1968
|
+
},
|
|
1969
|
+
"serveExport": "createRedditTwinServer"
|
|
1970
|
+
},
|
|
1847
1971
|
"resend": {
|
|
1848
1972
|
"adoption": {
|
|
1849
1973
|
"pypi": [
|
|
@@ -2817,27 +2941,27 @@
|
|
|
2817
2941
|
},
|
|
2818
2942
|
{
|
|
2819
2943
|
"host": "x.com",
|
|
2820
|
-
"pathPattern": "^/i/oauth2/authorize/?$|^/_twin/|^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}
|
|
2944
|
+
"pathPattern": "^/i/oauth2/authorize/?$|^/_twin/|^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}/?$|^/home/?$|^/i/flow/login/?$|^/$"
|
|
2821
2945
|
},
|
|
2822
2946
|
{
|
|
2823
2947
|
"host": "twitter.com",
|
|
2824
|
-
"pathPattern": "^/i/oauth2/authorize/?$|^/_twin/|^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}
|
|
2948
|
+
"pathPattern": "^/i/oauth2/authorize/?$|^/_twin/|^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}/?$|^/home/?$|^/i/flow/login/?$|^/$"
|
|
2825
2949
|
},
|
|
2826
2950
|
{
|
|
2827
2951
|
"host": "www.x.com",
|
|
2828
|
-
"pathPattern": "^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}
|
|
2952
|
+
"pathPattern": "^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}/?$|^/home/?$|^/i/flow/login/?$|^/$"
|
|
2829
2953
|
},
|
|
2830
2954
|
{
|
|
2831
2955
|
"host": "www.twitter.com",
|
|
2832
|
-
"pathPattern": "^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}
|
|
2956
|
+
"pathPattern": "^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}/?$|^/home/?$|^/i/flow/login/?$|^/$"
|
|
2833
2957
|
},
|
|
2834
2958
|
{
|
|
2835
2959
|
"host": "mobile.twitter.com",
|
|
2836
|
-
"pathPattern": "^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}
|
|
2960
|
+
"pathPattern": "^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}/?$|^/home/?$|^/i/flow/login/?$|^/$"
|
|
2837
2961
|
},
|
|
2838
2962
|
{
|
|
2839
2963
|
"host": "mobile.x.com",
|
|
2840
|
-
"pathPattern": "^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}
|
|
2964
|
+
"pathPattern": "^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}/?$|^/home/?$|^/i/flow/login/?$|^/$"
|
|
2841
2965
|
},
|
|
2842
2966
|
{
|
|
2843
2967
|
"host": "pbs.twimg.com",
|
|
@@ -2863,7 +2987,6 @@
|
|
|
2863
2987
|
"oauth_client",
|
|
2864
2988
|
"oauth1_app",
|
|
2865
2989
|
"account",
|
|
2866
|
-
"session",
|
|
2867
2990
|
"auth_request",
|
|
2868
2991
|
"post",
|
|
2869
2992
|
"rate_limit",
|
|
@@ -2873,6 +2996,7 @@
|
|
|
2873
2996
|
"repost",
|
|
2874
2997
|
"bookmark",
|
|
2875
2998
|
"_impressions",
|
|
2999
|
+
"_card",
|
|
2876
3000
|
"_grant",
|
|
2877
3001
|
"_oauth1_grant",
|
|
2878
3002
|
"_rate_window",
|
package/dist/inject.cjs
CHANGED
|
@@ -21,7 +21,13 @@
|
|
|
21
21
|
// (stripe-node → https; @octokit → fetch; @slack/web-api & jira.js → axios →
|
|
22
22
|
// http/https; @linear/sdk → fetch), so no per-SDK shim is needed.
|
|
23
23
|
//
|
|
24
|
-
//
|
|
24
|
+
// Attached to a World (VOLTER_WORLD), a request a bundled undici (wrangler's) sends to the
|
|
25
|
+
// World's own paths through undici's shared global dispatcher carries the World's key too:
|
|
26
|
+
// Object.defineProperty is watched for that global, and the dispatcher set there is stood
|
|
27
|
+
// in for by a view whose dispatch keys a hop whose origin and path are the World's
|
|
28
|
+
// (keyedView, below), sent with maxRedirections 0 so no follower below it takes the key on.
|
|
29
|
+
//
|
|
30
|
+
// A client can still open its own sockets (npm `undici`'s own Agent, which
|
|
25
31
|
// @turbopuffer/turbopuffer uses under Node, honours neither patch nor HTTPS_PROXY).
|
|
26
32
|
// `net.connect`/`tls.connect` are the seam every Node client ends at, so they carry
|
|
27
33
|
// the backstop: a connect to a twinned vendor host went around the rewrite and is
|
|
@@ -435,6 +441,74 @@ function withWorldKey(args) {
|
|
|
435
441
|
return out;
|
|
436
442
|
}
|
|
437
443
|
|
|
444
|
+
// A client that bundles its own undici (wrangler) calls neither the global `fetch` nor http(s), so its requests to the
|
|
445
|
+
// World's own paths would arrive without the key. Every undici copy, Node's own included, keeps its dispatcher on one
|
|
446
|
+
// shared global (`Symbol.for('undici.globalDispatcher.<n>')`), set with Object.defineProperty when that copy loads. A
|
|
447
|
+
// dispatcher defined there is stood in for by a view of it whose `dispatch` adds the key to one hop, only when that
|
|
448
|
+
// hop's origin is the World's and its path one of the World's own, compared as strings (a path is never resolved as a
|
|
449
|
+
// URL, so `//other-host/…` names no other origin); the dispatcher itself is never changed, and restore() puts it back.
|
|
450
|
+
const UNDICI_DISPATCHERS = [Symbol.for('undici.globalDispatcher.1'), Symbol.for('undici.globalDispatcher.2')];
|
|
451
|
+
const KEYED_VIEWS = new WeakMap(); // view -> the dispatcher it stands in for
|
|
452
|
+
function keyedUndiciHeaders(headers, key) {
|
|
453
|
+
if (headers == null) return { 'x-twins-key': key };
|
|
454
|
+
if (Array.isArray(headers)) {
|
|
455
|
+
const flat = headers.length === 0 || !Array.isArray(headers[0]);
|
|
456
|
+
const names = flat ? headers.filter((_, i) => i % 2 === 0) : headers.map((h) => h[0]);
|
|
457
|
+
if (names.some((n) => String(n).toLowerCase() === 'x-twins-key')) return headers;
|
|
458
|
+
return flat ? [...headers, 'x-twins-key', key] : [...headers, ['x-twins-key', key]];
|
|
459
|
+
}
|
|
460
|
+
const entries = typeof headers[Symbol.iterator] === 'function' ? [...headers] : Object.entries(headers);
|
|
461
|
+
if (entries.some(([n]) => String(n).toLowerCase() === 'x-twins-key')) return headers;
|
|
462
|
+
return Object.assign(Object.fromEntries(entries), { 'x-twins-key': key });
|
|
463
|
+
}
|
|
464
|
+
/** A dispatch hop is the World's: its origin is the World's own and its path, as written, under the World's paths. */
|
|
465
|
+
function hopToWorld(opts) {
|
|
466
|
+
const base = worldBase();
|
|
467
|
+
if (!base || !base.path || !opts || opts.origin == null) return false;
|
|
468
|
+
let origin;
|
|
469
|
+
try { origin = new URL(String(opts.origin)).origin; } catch { return false; }
|
|
470
|
+
const at = String(opts.path == null ? '/' : opts.path);
|
|
471
|
+
return origin === base.origin && (at.startsWith(`${base.path}/`) || at.startsWith(`/-${base.path}/`));
|
|
472
|
+
}
|
|
473
|
+
function keyedView(dispatcher) {
|
|
474
|
+
if (!dispatcher || typeof dispatcher.dispatch !== 'function' || KEYED_VIEWS.has(dispatcher)) return dispatcher;
|
|
475
|
+
const view = new Proxy(dispatcher, {
|
|
476
|
+
get(target, name) {
|
|
477
|
+
if (name === 'dispatch') {
|
|
478
|
+
return function keyedDispatch(opts, handler) {
|
|
479
|
+
const key = ORIGINAL_DEFINE_PROPERTY ? twinsKey() : null;
|
|
480
|
+
// a hop the caller asked undici to follow is not keyed; a keyed hop is sent with maxRedirections 0, so no
|
|
481
|
+
// redirect follower below this view (a composed interceptor, a RetryAgent or proxy agent around one, undici
|
|
482
|
+
// 6's option or interceptors) carries its headers to the next origin: the redirect comes back to the caller
|
|
483
|
+
const keyed = key && !(opts && Number(opts.maxRedirections) > 0) && hopToWorld(opts);
|
|
484
|
+
return target.dispatch(keyed ? Object.assign({}, opts, { headers: keyedUndiciHeaders(opts.headers, key), maxRedirections: 0 }) : opts, handler);
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
const value = Reflect.get(target, name, target);
|
|
488
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
489
|
+
},
|
|
490
|
+
});
|
|
491
|
+
KEYED_VIEWS.set(view, dispatcher);
|
|
492
|
+
return view;
|
|
493
|
+
}
|
|
494
|
+
let ORIGINAL_DEFINE_PROPERTY = null;
|
|
495
|
+
function keyUndiciDispatches() {
|
|
496
|
+
if (ORIGINAL_DEFINE_PROPERTY || !worldBase() || Object.defineProperty.name === 'keyingDefineProperty') return;
|
|
497
|
+
const define = ORIGINAL_DEFINE_PROPERTY = Object.defineProperty;
|
|
498
|
+
Object.defineProperty = function keyingDefineProperty(target, name, descriptor) {
|
|
499
|
+
if (ORIGINAL_DEFINE_PROPERTY && target === globalThis && UNDICI_DISPATCHERS.includes(name) && descriptor && 'value' in descriptor) descriptor = Object.assign({}, descriptor, { value: keyedView(descriptor.value) });
|
|
500
|
+
return define(target, name, descriptor);
|
|
501
|
+
};
|
|
502
|
+
for (const name of UNDICI_DISPATCHERS) if (globalThis[name]) globalThis[name] = keyedView(globalThis[name]);
|
|
503
|
+
}
|
|
504
|
+
function unkeyUndiciDispatches() {
|
|
505
|
+
if (!ORIGINAL_DEFINE_PROPERTY) return;
|
|
506
|
+
// only what this put in place is put back: a wrapper installed over it since stays
|
|
507
|
+
if (Object.defineProperty.name === 'keyingDefineProperty') Object.defineProperty = ORIGINAL_DEFINE_PROPERTY;
|
|
508
|
+
ORIGINAL_DEFINE_PROPERTY = null;
|
|
509
|
+
for (const name of UNDICI_DISPATCHERS) if (KEYED_VIEWS.has(globalThis[name])) globalThis[name] = KEYED_VIEWS.get(globalThis[name]);
|
|
510
|
+
}
|
|
511
|
+
|
|
438
512
|
// S4 review F3: undici's default redirect-follow forwards CUSTOM headers across origins — a
|
|
439
513
|
// twin (or the World) answering 30x to an external URL would carry x-twins-key to a REAL host. A
|
|
440
514
|
// keyed fetch follows redirects itself: a hop that is a twin or the World re-enters the patched
|
|
@@ -979,6 +1053,7 @@ function install(mapOverride) {
|
|
|
979
1053
|
tls.connect = patchedConnect(ORIGINALS.tlsConnect, true);
|
|
980
1054
|
dns.lookup = patchedLookup(ORIGINALS.dnsLookup);
|
|
981
1055
|
dns.promises.lookup = patchedPromisesLookup(ORIGINALS.dnsPromisesLookup);
|
|
1056
|
+
keyUndiciDispatches();
|
|
982
1057
|
// gRPC (architecture, "Other wires": gRPC): an HTTP/2 session to a host a twin serves (grpc-js on google-gax: TLS to
|
|
983
1058
|
// webrisk.googleapis.com) connects to that twin's origin in plain text, where the kernel's HTTP/2 server answers it;
|
|
984
1059
|
// the client carries on unchanged. Any other authority is the real one's.
|
|
@@ -1139,6 +1214,7 @@ function restore() {
|
|
|
1139
1214
|
dns.lookup = ORIGINALS.dnsLookup;
|
|
1140
1215
|
dns.promises.lookup = ORIGINALS.dnsPromisesLookup;
|
|
1141
1216
|
http2.connect = ORIGINALS.http2Connect;
|
|
1217
|
+
unkeyUndiciDispatches();
|
|
1142
1218
|
installed = false;
|
|
1143
1219
|
MAP = {};
|
|
1144
1220
|
}
|
|
@@ -1137,6 +1137,48 @@
|
|
|
1137
1137
|
},
|
|
1138
1138
|
"serveExport": "createGroqTwinServer"
|
|
1139
1139
|
},
|
|
1140
|
+
"hackernews": {
|
|
1141
|
+
"adoption": {
|
|
1142
|
+
"sdks": [],
|
|
1143
|
+
"envStems": [
|
|
1144
|
+
"HN",
|
|
1145
|
+
"HACKERNEWS"
|
|
1146
|
+
]
|
|
1147
|
+
},
|
|
1148
|
+
"hosts": [
|
|
1149
|
+
{
|
|
1150
|
+
"host": "hacker-news.firebaseio.com"
|
|
1151
|
+
},
|
|
1152
|
+
{
|
|
1153
|
+
"host": "news.ycombinator.com"
|
|
1154
|
+
}
|
|
1155
|
+
],
|
|
1156
|
+
"archetype": "crud",
|
|
1157
|
+
"protocol": {
|
|
1158
|
+
"declared": "3",
|
|
1159
|
+
"major": 3,
|
|
1160
|
+
"standing": "current"
|
|
1161
|
+
},
|
|
1162
|
+
"transport": "rest",
|
|
1163
|
+
"resources": [
|
|
1164
|
+
"item",
|
|
1165
|
+
"user",
|
|
1166
|
+
"_vote",
|
|
1167
|
+
"_person",
|
|
1168
|
+
"_web_session",
|
|
1169
|
+
"_web_session_issue"
|
|
1170
|
+
],
|
|
1171
|
+
"endpointEnvNone": "Hacker News publishes no SDK: clients fetch https://hacker-news.firebaseio.com/v0 directly and people use news.ycombinator.com in a browser, both reached by host",
|
|
1172
|
+
"credentialDoor": {
|
|
1173
|
+
"path": "/_twin/app-credentials",
|
|
1174
|
+
"body": {},
|
|
1175
|
+
"fill": {
|
|
1176
|
+
"HN_USERNAME": "acct",
|
|
1177
|
+
"HN_PASSWORD": "pw"
|
|
1178
|
+
}
|
|
1179
|
+
},
|
|
1180
|
+
"serveExport": "createHackernewsTwinServer"
|
|
1181
|
+
},
|
|
1140
1182
|
"hubspot": {
|
|
1141
1183
|
"adoption": {
|
|
1142
1184
|
"sdks": [
|
|
@@ -1844,6 +1886,88 @@
|
|
|
1844
1886
|
},
|
|
1845
1887
|
"serveExport": "createPosthogTwinServer"
|
|
1846
1888
|
},
|
|
1889
|
+
"reddit": {
|
|
1890
|
+
"adoption": {
|
|
1891
|
+
"sdks": [
|
|
1892
|
+
"snoowrap"
|
|
1893
|
+
],
|
|
1894
|
+
"pypi": [
|
|
1895
|
+
"praw",
|
|
1896
|
+
"asyncpraw"
|
|
1897
|
+
],
|
|
1898
|
+
"envStems": [
|
|
1899
|
+
"REDDIT"
|
|
1900
|
+
]
|
|
1901
|
+
},
|
|
1902
|
+
"hosts": [
|
|
1903
|
+
{
|
|
1904
|
+
"host": "oauth.reddit.com"
|
|
1905
|
+
},
|
|
1906
|
+
{
|
|
1907
|
+
"host": "www.reddit.com"
|
|
1908
|
+
},
|
|
1909
|
+
{
|
|
1910
|
+
"host": "reddit.com"
|
|
1911
|
+
},
|
|
1912
|
+
{
|
|
1913
|
+
"host": "reddit-uploaded-media.s3-accelerate.amazonaws.com"
|
|
1914
|
+
},
|
|
1915
|
+
{
|
|
1916
|
+
"host": "reddit-uploaded-video.s3-accelerate.amazonaws.com"
|
|
1917
|
+
},
|
|
1918
|
+
{
|
|
1919
|
+
"host": "v.redd.it"
|
|
1920
|
+
},
|
|
1921
|
+
{
|
|
1922
|
+
"host": "i.redd.it"
|
|
1923
|
+
},
|
|
1924
|
+
{
|
|
1925
|
+
"host": "preview.redd.it"
|
|
1926
|
+
},
|
|
1927
|
+
{
|
|
1928
|
+
"host": "external-preview.redd.it"
|
|
1929
|
+
},
|
|
1930
|
+
{
|
|
1931
|
+
"host": "b.thumbs.redditmedia.com"
|
|
1932
|
+
}
|
|
1933
|
+
],
|
|
1934
|
+
"archetype": "crud",
|
|
1935
|
+
"protocol": {
|
|
1936
|
+
"declared": "3",
|
|
1937
|
+
"major": 3,
|
|
1938
|
+
"standing": "current"
|
|
1939
|
+
},
|
|
1940
|
+
"transport": "rest",
|
|
1941
|
+
"resourcesUnreachable": {},
|
|
1942
|
+
"resources": [
|
|
1943
|
+
"account",
|
|
1944
|
+
"subreddit",
|
|
1945
|
+
"link",
|
|
1946
|
+
"comment",
|
|
1947
|
+
"asset",
|
|
1948
|
+
"app",
|
|
1949
|
+
"_access_token",
|
|
1950
|
+
"_token_issue",
|
|
1951
|
+
"_client_secret",
|
|
1952
|
+
"_rate_window",
|
|
1953
|
+
"_vote",
|
|
1954
|
+
"_membership",
|
|
1955
|
+
"_person",
|
|
1956
|
+
"_web_session",
|
|
1957
|
+
"_web_session_issue"
|
|
1958
|
+
],
|
|
1959
|
+
"endpointEnvNone": "Reddit's clients name oauth.reddit.com and www.reddit.com themselves (the campaign's sender writes them as constants); the World's injector routes those hosts, and no client of the demand reads a base-URL variable",
|
|
1960
|
+
"credentialDoor": {
|
|
1961
|
+
"path": "/_twin/app-credentials",
|
|
1962
|
+
"body": {
|
|
1963
|
+
"username": "volter"
|
|
1964
|
+
},
|
|
1965
|
+
"fill": {
|
|
1966
|
+
"REDDIT_ACCESS_TOKEN": "access_token"
|
|
1967
|
+
}
|
|
1968
|
+
},
|
|
1969
|
+
"serveExport": "createRedditTwinServer"
|
|
1970
|
+
},
|
|
1847
1971
|
"resend": {
|
|
1848
1972
|
"adoption": {
|
|
1849
1973
|
"pypi": [
|
|
@@ -2817,27 +2941,27 @@
|
|
|
2817
2941
|
},
|
|
2818
2942
|
{
|
|
2819
2943
|
"host": "x.com",
|
|
2820
|
-
"pathPattern": "^/i/oauth2/authorize/?$|^/_twin/|^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}
|
|
2944
|
+
"pathPattern": "^/i/oauth2/authorize/?$|^/_twin/|^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}/?$|^/home/?$|^/i/flow/login/?$|^/$"
|
|
2821
2945
|
},
|
|
2822
2946
|
{
|
|
2823
2947
|
"host": "twitter.com",
|
|
2824
|
-
"pathPattern": "^/i/oauth2/authorize/?$|^/_twin/|^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}
|
|
2948
|
+
"pathPattern": "^/i/oauth2/authorize/?$|^/_twin/|^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}/?$|^/home/?$|^/i/flow/login/?$|^/$"
|
|
2825
2949
|
},
|
|
2826
2950
|
{
|
|
2827
2951
|
"host": "www.x.com",
|
|
2828
|
-
"pathPattern": "^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}
|
|
2952
|
+
"pathPattern": "^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}/?$|^/home/?$|^/i/flow/login/?$|^/$"
|
|
2829
2953
|
},
|
|
2830
2954
|
{
|
|
2831
2955
|
"host": "www.twitter.com",
|
|
2832
|
-
"pathPattern": "^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}
|
|
2956
|
+
"pathPattern": "^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}/?$|^/home/?$|^/i/flow/login/?$|^/$"
|
|
2833
2957
|
},
|
|
2834
2958
|
{
|
|
2835
2959
|
"host": "mobile.twitter.com",
|
|
2836
|
-
"pathPattern": "^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}
|
|
2960
|
+
"pathPattern": "^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}/?$|^/home/?$|^/i/flow/login/?$|^/$"
|
|
2837
2961
|
},
|
|
2838
2962
|
{
|
|
2839
2963
|
"host": "mobile.x.com",
|
|
2840
|
-
"pathPattern": "^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}
|
|
2964
|
+
"pathPattern": "^/(?!_twin/)[A-Za-z0-9_]{1,15}/status/[0-9]{1,19}/?$|^/(?!(?:i|en|home|explore|notifications|messages|settings|search|compose|login|logout|signup|tos|privacy|about|account|intent|share|hashtag|lists|bookmarks|jobs|download|grok|premium|communities|creators|twin|_twin)/?$)[A-Za-z0-9_]{1,15}/?$|^/home/?$|^/i/flow/login/?$|^/$"
|
|
2841
2965
|
},
|
|
2842
2966
|
{
|
|
2843
2967
|
"host": "pbs.twimg.com",
|
|
@@ -2863,7 +2987,6 @@
|
|
|
2863
2987
|
"oauth_client",
|
|
2864
2988
|
"oauth1_app",
|
|
2865
2989
|
"account",
|
|
2866
|
-
"session",
|
|
2867
2990
|
"auth_request",
|
|
2868
2991
|
"post",
|
|
2869
2992
|
"rate_limit",
|
|
@@ -2873,6 +2996,7 @@
|
|
|
2873
2996
|
"repost",
|
|
2874
2997
|
"bookmark",
|
|
2875
2998
|
"_impressions",
|
|
2999
|
+
"_card",
|
|
2876
3000
|
"_grant",
|
|
2877
3001
|
"_oauth1_grant",
|
|
2878
3002
|
"_rate_window",
|
package/inject.cjs
CHANGED
|
@@ -21,7 +21,13 @@
|
|
|
21
21
|
// (stripe-node → https; @octokit → fetch; @slack/web-api & jira.js → axios →
|
|
22
22
|
// http/https; @linear/sdk → fetch), so no per-SDK shim is needed.
|
|
23
23
|
//
|
|
24
|
-
//
|
|
24
|
+
// Attached to a World (VOLTER_WORLD), a request a bundled undici (wrangler's) sends to the
|
|
25
|
+
// World's own paths through undici's shared global dispatcher carries the World's key too:
|
|
26
|
+
// Object.defineProperty is watched for that global, and the dispatcher set there is stood
|
|
27
|
+
// in for by a view whose dispatch keys a hop whose origin and path are the World's
|
|
28
|
+
// (keyedView, below), sent with maxRedirections 0 so no follower below it takes the key on.
|
|
29
|
+
//
|
|
30
|
+
// A client can still open its own sockets (npm `undici`'s own Agent, which
|
|
25
31
|
// @turbopuffer/turbopuffer uses under Node, honours neither patch nor HTTPS_PROXY).
|
|
26
32
|
// `net.connect`/`tls.connect` are the seam every Node client ends at, so they carry
|
|
27
33
|
// the backstop: a connect to a twinned vendor host went around the rewrite and is
|
|
@@ -435,6 +441,74 @@ function withWorldKey(args) {
|
|
|
435
441
|
return out;
|
|
436
442
|
}
|
|
437
443
|
|
|
444
|
+
// A client that bundles its own undici (wrangler) calls neither the global `fetch` nor http(s), so its requests to the
|
|
445
|
+
// World's own paths would arrive without the key. Every undici copy, Node's own included, keeps its dispatcher on one
|
|
446
|
+
// shared global (`Symbol.for('undici.globalDispatcher.<n>')`), set with Object.defineProperty when that copy loads. A
|
|
447
|
+
// dispatcher defined there is stood in for by a view of it whose `dispatch` adds the key to one hop, only when that
|
|
448
|
+
// hop's origin is the World's and its path one of the World's own, compared as strings (a path is never resolved as a
|
|
449
|
+
// URL, so `//other-host/…` names no other origin); the dispatcher itself is never changed, and restore() puts it back.
|
|
450
|
+
const UNDICI_DISPATCHERS = [Symbol.for('undici.globalDispatcher.1'), Symbol.for('undici.globalDispatcher.2')];
|
|
451
|
+
const KEYED_VIEWS = new WeakMap(); // view -> the dispatcher it stands in for
|
|
452
|
+
function keyedUndiciHeaders(headers, key) {
|
|
453
|
+
if (headers == null) return { 'x-twins-key': key };
|
|
454
|
+
if (Array.isArray(headers)) {
|
|
455
|
+
const flat = headers.length === 0 || !Array.isArray(headers[0]);
|
|
456
|
+
const names = flat ? headers.filter((_, i) => i % 2 === 0) : headers.map((h) => h[0]);
|
|
457
|
+
if (names.some((n) => String(n).toLowerCase() === 'x-twins-key')) return headers;
|
|
458
|
+
return flat ? [...headers, 'x-twins-key', key] : [...headers, ['x-twins-key', key]];
|
|
459
|
+
}
|
|
460
|
+
const entries = typeof headers[Symbol.iterator] === 'function' ? [...headers] : Object.entries(headers);
|
|
461
|
+
if (entries.some(([n]) => String(n).toLowerCase() === 'x-twins-key')) return headers;
|
|
462
|
+
return Object.assign(Object.fromEntries(entries), { 'x-twins-key': key });
|
|
463
|
+
}
|
|
464
|
+
/** A dispatch hop is the World's: its origin is the World's own and its path, as written, under the World's paths. */
|
|
465
|
+
function hopToWorld(opts) {
|
|
466
|
+
const base = worldBase();
|
|
467
|
+
if (!base || !base.path || !opts || opts.origin == null) return false;
|
|
468
|
+
let origin;
|
|
469
|
+
try { origin = new URL(String(opts.origin)).origin; } catch { return false; }
|
|
470
|
+
const at = String(opts.path == null ? '/' : opts.path);
|
|
471
|
+
return origin === base.origin && (at.startsWith(`${base.path}/`) || at.startsWith(`/-${base.path}/`));
|
|
472
|
+
}
|
|
473
|
+
function keyedView(dispatcher) {
|
|
474
|
+
if (!dispatcher || typeof dispatcher.dispatch !== 'function' || KEYED_VIEWS.has(dispatcher)) return dispatcher;
|
|
475
|
+
const view = new Proxy(dispatcher, {
|
|
476
|
+
get(target, name) {
|
|
477
|
+
if (name === 'dispatch') {
|
|
478
|
+
return function keyedDispatch(opts, handler) {
|
|
479
|
+
const key = ORIGINAL_DEFINE_PROPERTY ? twinsKey() : null;
|
|
480
|
+
// a hop the caller asked undici to follow is not keyed; a keyed hop is sent with maxRedirections 0, so no
|
|
481
|
+
// redirect follower below this view (a composed interceptor, a RetryAgent or proxy agent around one, undici
|
|
482
|
+
// 6's option or interceptors) carries its headers to the next origin: the redirect comes back to the caller
|
|
483
|
+
const keyed = key && !(opts && Number(opts.maxRedirections) > 0) && hopToWorld(opts);
|
|
484
|
+
return target.dispatch(keyed ? Object.assign({}, opts, { headers: keyedUndiciHeaders(opts.headers, key), maxRedirections: 0 }) : opts, handler);
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
const value = Reflect.get(target, name, target);
|
|
488
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
489
|
+
},
|
|
490
|
+
});
|
|
491
|
+
KEYED_VIEWS.set(view, dispatcher);
|
|
492
|
+
return view;
|
|
493
|
+
}
|
|
494
|
+
let ORIGINAL_DEFINE_PROPERTY = null;
|
|
495
|
+
function keyUndiciDispatches() {
|
|
496
|
+
if (ORIGINAL_DEFINE_PROPERTY || !worldBase() || Object.defineProperty.name === 'keyingDefineProperty') return;
|
|
497
|
+
const define = ORIGINAL_DEFINE_PROPERTY = Object.defineProperty;
|
|
498
|
+
Object.defineProperty = function keyingDefineProperty(target, name, descriptor) {
|
|
499
|
+
if (ORIGINAL_DEFINE_PROPERTY && target === globalThis && UNDICI_DISPATCHERS.includes(name) && descriptor && 'value' in descriptor) descriptor = Object.assign({}, descriptor, { value: keyedView(descriptor.value) });
|
|
500
|
+
return define(target, name, descriptor);
|
|
501
|
+
};
|
|
502
|
+
for (const name of UNDICI_DISPATCHERS) if (globalThis[name]) globalThis[name] = keyedView(globalThis[name]);
|
|
503
|
+
}
|
|
504
|
+
function unkeyUndiciDispatches() {
|
|
505
|
+
if (!ORIGINAL_DEFINE_PROPERTY) return;
|
|
506
|
+
// only what this put in place is put back: a wrapper installed over it since stays
|
|
507
|
+
if (Object.defineProperty.name === 'keyingDefineProperty') Object.defineProperty = ORIGINAL_DEFINE_PROPERTY;
|
|
508
|
+
ORIGINAL_DEFINE_PROPERTY = null;
|
|
509
|
+
for (const name of UNDICI_DISPATCHERS) if (KEYED_VIEWS.has(globalThis[name])) globalThis[name] = KEYED_VIEWS.get(globalThis[name]);
|
|
510
|
+
}
|
|
511
|
+
|
|
438
512
|
// S4 review F3: undici's default redirect-follow forwards CUSTOM headers across origins — a
|
|
439
513
|
// twin (or the World) answering 30x to an external URL would carry x-twins-key to a REAL host. A
|
|
440
514
|
// keyed fetch follows redirects itself: a hop that is a twin or the World re-enters the patched
|
|
@@ -979,6 +1053,7 @@ function install(mapOverride) {
|
|
|
979
1053
|
tls.connect = patchedConnect(ORIGINALS.tlsConnect, true);
|
|
980
1054
|
dns.lookup = patchedLookup(ORIGINALS.dnsLookup);
|
|
981
1055
|
dns.promises.lookup = patchedPromisesLookup(ORIGINALS.dnsPromisesLookup);
|
|
1056
|
+
keyUndiciDispatches();
|
|
982
1057
|
// gRPC (architecture, "Other wires": gRPC): an HTTP/2 session to a host a twin serves (grpc-js on google-gax: TLS to
|
|
983
1058
|
// webrisk.googleapis.com) connects to that twin's origin in plain text, where the kernel's HTTP/2 server answers it;
|
|
984
1059
|
// the client carries on unchanged. Any other authority is the real one's.
|
|
@@ -1139,6 +1214,7 @@ function restore() {
|
|
|
1139
1214
|
dns.lookup = ORIGINALS.dnsLookup;
|
|
1140
1215
|
dns.promises.lookup = ORIGINALS.dnsPromisesLookup;
|
|
1141
1216
|
http2.connect = ORIGINALS.http2Connect;
|
|
1217
|
+
unkeyUndiciDispatches();
|
|
1142
1218
|
installed = false;
|
|
1143
1219
|
MAP = {};
|
|
1144
1220
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/world-core",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.21",
|
|
4
4
|
"description": "The kernel of Volter World: one log per twin, branches as pointers, checkpoints, the fold that keeps a twin current, the head that performs a write against the vendor, references, and the git plane. A twin package builds on it; the runtime serves it.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"twin",
|