sandboxedjs 0.1.91 → 0.1.93

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/index.cjs CHANGED
@@ -20031,6 +20031,67 @@ var wheels_default = {
20031
20031
  ]
20032
20032
  }
20033
20033
  },
20034
+ {
20035
+ name: "argon2-cffi-bindings",
20036
+ version: "21.2.0",
20037
+ filename: "argon2_cffi_bindings-21.2.0-cp313-cp313-emscripten_5_0_6_wasm32.whl",
20038
+ sha256: "16a0bb095ad8c966509752c2f0498bea965839deead4d84ad4badf6465b5948b",
20039
+ abiId: "sbxabi1-c2637d04695ad927",
20040
+ requires: [
20041
+ "cffi>=1.0.1",
20042
+ 'pytest; extra == "tests"',
20043
+ 'pytest; extra == "dev"',
20044
+ 'cogapp; extra == "dev"',
20045
+ 'pre-commit; extra == "dev"',
20046
+ 'wheel; extra == "dev"'
20047
+ ],
20048
+ provenance: {
20049
+ sourceTreeDigest: "8093e19a848187cbd5a92c541c1dab627f0bacb7ac8d09216d0226a313d6f9ab",
20050
+ recipeSha256: "3744603cabcf8107c497d8e6d106614135ef0dfeec5d32ac24039b7e8e4d9009",
20051
+ buildTools: [
20052
+ "setuptools==84.0.0",
20053
+ "cffi==2.1.1",
20054
+ "setuptools-scm==10.2.3"
20055
+ ],
20056
+ nativeDependencies: [],
20057
+ patches: []
20058
+ }
20059
+ },
20060
+ {
20061
+ name: "bcrypt",
20062
+ version: "5.0.0",
20063
+ filename: "bcrypt-5.0.0-cp313-cp313-emscripten_5_0_6_wasm32.whl",
20064
+ sha256: "a8fdcaccccf931b2fb3e327f94e8dc9a8e7c53bf17bc9fb85aae25211cbac61f",
20065
+ abiId: "sbxabi1-c2637d04695ad927",
20066
+ requires: [],
20067
+ provenance: {
20068
+ sourceTreeDigest: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
20069
+ recipeSha256: "3a1af92dc00b0d1896cf1483399c002df4b36289de7dea2324035407e78e9f9b",
20070
+ buildTools: [
20071
+ "setuptools==84.0.0"
20072
+ ],
20073
+ nativeDependencies: [],
20074
+ patches: []
20075
+ }
20076
+ },
20077
+ {
20078
+ name: "brotli",
20079
+ version: "1.2.0",
20080
+ filename: "brotli-1.2.0-cp313-cp313-emscripten_5_0_6_wasm32.whl",
20081
+ sha256: "b67d1b9169ba3e0790d04b8020adb1a58f0b89f8961aca360dca5f020de07a21",
20082
+ abiId: "sbxabi1-c2637d04695ad927",
20083
+ requires: [],
20084
+ provenance: {
20085
+ sourceTreeDigest: "7b563fa2e7f20edac204cc80004cae35c844b36a83758e5957184383332d1209",
20086
+ recipeSha256: "09bf4f6072d637866a6b3c3deaec419dc3ff8a32a6f3b0813f57e06316bfcf39",
20087
+ buildTools: [
20088
+ "setuptools==84.0.0",
20089
+ "pkgconfig==1.6.0"
20090
+ ],
20091
+ nativeDependencies: [],
20092
+ patches: []
20093
+ }
20094
+ },
20034
20095
  {
20035
20096
  name: "cffi",
20036
20097
  version: "2.1.1",
@@ -20052,6 +20113,26 @@ var wheels_default = {
20052
20113
  patches: []
20053
20114
  }
20054
20115
  },
20116
+ {
20117
+ name: "cryptography",
20118
+ version: "50.0.1",
20119
+ filename: "cryptography-50.0.1-cp313-cp313-emscripten_5_0_6_wasm32.whl",
20120
+ sha256: "10bb6568bf4368a593ea3a990ea870e57d4eeb1d3278af89bf24a8713473238e",
20121
+ abiId: "sbxabi1-c2637d04695ad927",
20122
+ requires: [
20123
+ 'cffi>=2.0.0; platform_python_implementation != "PyPy"'
20124
+ ],
20125
+ provenance: {
20126
+ sourceTreeDigest: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
20127
+ recipeSha256: "082169d1d317ba001e37ccfb7a40a0dafd8d3d34e42034da8590f0207ae73ea5",
20128
+ buildTools: [
20129
+ "setuptools==84.0.0",
20130
+ "cffi==2.1.1"
20131
+ ],
20132
+ nativeDependencies: [],
20133
+ patches: []
20134
+ }
20135
+ },
20055
20136
  {
20056
20137
  name: "fastar",
20057
20138
  version: "0.12.0",
@@ -20105,6 +20186,32 @@ var wheels_default = {
20105
20186
  patches: []
20106
20187
  }
20107
20188
  },
20189
+ {
20190
+ name: "lz4",
20191
+ version: "4.4.5",
20192
+ filename: "lz4-4.4.5-cp313-cp313-emscripten_5_0_6_wasm32.whl",
20193
+ sha256: "f94c0ef7d84c9bf700cc86f69a28382699262e442e29d9763736afdb61141ffe",
20194
+ abiId: "sbxabi1-c2637d04695ad927",
20195
+ requires: [
20196
+ 'pytest!=3.3.0; extra == "tests"',
20197
+ 'psutil; extra == "tests"',
20198
+ 'pytest-cov; extra == "tests"',
20199
+ 'sphinx>=1.6.0; extra == "docs"',
20200
+ 'sphinx_bootstrap_theme; extra == "docs"',
20201
+ 'flake8; extra == "flake8"'
20202
+ ],
20203
+ provenance: {
20204
+ sourceTreeDigest: "5f7bda9bfb026d0a30bb5e3383a4b25ae6b7f44fd6dc74950653d57f84c9d88e",
20205
+ recipeSha256: "80250de0ae159cd9f977bdfad059de0d192f28555225b084b42b5a180f684e0c",
20206
+ buildTools: [
20207
+ "setuptools==84.0.0",
20208
+ "pkgconfig==1.6.0",
20209
+ "setuptools-scm==10.2.3"
20210
+ ],
20211
+ nativeDependencies: [],
20212
+ patches: []
20213
+ }
20214
+ },
20108
20215
  {
20109
20216
  name: "msgpack",
20110
20217
  version: "1.1.0",
@@ -20144,20 +20251,86 @@ var wheels_default = {
20144
20251
  patches: []
20145
20252
  }
20146
20253
  },
20254
+ {
20255
+ name: "orjson",
20256
+ version: "3.12.0",
20257
+ filename: "orjson-3.12.0-cp313-cp313-emscripten_5_0_6_wasm32.whl",
20258
+ sha256: "1a8b9b3ab496f6d8b1ef73d0ae282262d8399f55243bf01f2dbfac46aff99a35",
20259
+ abiId: "sbxabi1-c2637d04695ad927",
20260
+ requires: [],
20261
+ provenance: {
20262
+ sourceTreeDigest: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
20263
+ recipeSha256: "cda75f871a972e759361a12baeaa3531dc9c68c958556674fd30f3d639fb6b45",
20264
+ buildTools: [
20265
+ "setuptools==84.0.0"
20266
+ ],
20267
+ nativeDependencies: [],
20268
+ patches: []
20269
+ }
20270
+ },
20147
20271
  {
20148
20272
  name: "ormsgpack",
20149
20273
  version: "1.12.2",
20150
20274
  filename: "ormsgpack-1.12.2-cp313-cp313-emscripten_5_0_6_wasm32.whl",
20151
- sha256: "42ca38d1948a36d43294c7843fd9398f1ae88bddd9e56b26ad80a644f9bdec52",
20275
+ sha256: "7fa4463a69aad2fe057c111642cce71c8f72af8465c22769d839415dbc15ac79",
20152
20276
  abiId: "sbxabi1-c2637d04695ad927",
20153
20277
  requires: [],
20154
20278
  provenance: {
20155
- sourceTreeDigest: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
20156
- recipeSha256: "6ab618fe6bc9dee1fa76f57f298c55732fe2241e122240f44007a2ce0025e1b2",
20279
+ sourceTreeDigest: "eff3a7be3bc6de2a15e479c0e3e94905c35203fd83369124073f2df5cce46409",
20280
+ recipeSha256: "bd398762801fce51d35b33439f5e57037e1d4d274f25bd795683e2ad17d34b62",
20157
20281
  buildTools: [
20158
20282
  "setuptools==84.0.0"
20159
20283
  ],
20160
20284
  nativeDependencies: [],
20285
+ patches: [
20286
+ "0001-pylong-asbytearray-313-signature.patch"
20287
+ ]
20288
+ }
20289
+ },
20290
+ {
20291
+ name: "pillow",
20292
+ version: "12.3.0",
20293
+ filename: "pillow-12.3.0-cp313-cp313-emscripten_5_0_6_wasm32.whl",
20294
+ sha256: "7e585c71bf943029d8201283308a1b43f2be5f166d9beb36697d1f6bfba865e9",
20295
+ abiId: "sbxabi1-c2637d04695ad927",
20296
+ requires: [
20297
+ 'furo; extra == "docs"',
20298
+ 'olefile; extra == "docs"',
20299
+ 'sphinx>=8.2; extra == "docs"',
20300
+ 'sphinx-autobuild; extra == "docs"',
20301
+ 'sphinx-copybutton; extra == "docs"',
20302
+ 'sphinx-inline-tabs; extra == "docs"',
20303
+ 'sphinxext-opengraph; extra == "docs"',
20304
+ 'olefile; extra == "fpx"',
20305
+ 'olefile; extra == "mic"',
20306
+ 'arro3-compute; extra == "test-arrow"',
20307
+ 'arro3-core; extra == "test-arrow"',
20308
+ 'nanoarrow; extra == "test-arrow"',
20309
+ 'pyarrow; extra == "test-arrow"',
20310
+ 'coverage>=7.4.2; extra == "tests"',
20311
+ 'defusedxml; extra == "tests"',
20312
+ 'markdown2; extra == "tests"',
20313
+ 'olefile; extra == "tests"',
20314
+ 'packaging; extra == "tests"',
20315
+ 'pytest; extra == "tests"',
20316
+ 'pytest-cov; extra == "tests"',
20317
+ 'pytest-timeout; extra == "tests"',
20318
+ 'pytest-xdist; extra == "tests"',
20319
+ 'setuptools; extra == "tests"',
20320
+ 'trove-classifiers>=2024.10.12; extra == "tests"',
20321
+ 'defusedxml; extra == "xmp"'
20322
+ ],
20323
+ provenance: {
20324
+ sourceTreeDigest: "5427eff55a6a0e5f667d70527c2c3749f2c0786598903d7bbe7aaf16c4e95e31",
20325
+ recipeSha256: "77418b7e139e4779f2a422a06abbea27c941174b1f86597a2f3c4483bb236b7f",
20326
+ buildTools: [
20327
+ "setuptools==84.0.0",
20328
+ "pybind11==3.1.0"
20329
+ ],
20330
+ nativeDependencies: [
20331
+ "z==1.3.1",
20332
+ "jpeg==9f"
20333
+ ],
20161
20334
  patches: []
20162
20335
  }
20163
20336
  },
@@ -20218,6 +20391,23 @@ var wheels_default = {
20218
20391
  patches: []
20219
20392
  }
20220
20393
  },
20394
+ {
20395
+ name: "regex",
20396
+ version: "2026.9.10",
20397
+ filename: "regex-2026.9.10-cp313-cp313-emscripten_5_0_6_wasm32.whl",
20398
+ sha256: "44995aafa6294c4c817ff658755b931d936b0c00db6e4b086b6469a76938f7c8",
20399
+ abiId: "sbxabi1-c2637d04695ad927",
20400
+ requires: [],
20401
+ provenance: {
20402
+ sourceTreeDigest: "db71e2c60e74953662c35d3ce3f75893ac7a9df8190afecc916f1a73981c50f5",
20403
+ recipeSha256: "5fb970f2bdc3dfe0be8f98b04caa083c6a9088351d1415d4f9f49d154c7df90d",
20404
+ buildTools: [
20405
+ "setuptools==84.0.0"
20406
+ ],
20407
+ nativeDependencies: [],
20408
+ patches: []
20409
+ }
20410
+ },
20221
20411
  {
20222
20412
  name: "rignore",
20223
20413
  version: "0.8.1",
@@ -20406,6 +20596,23 @@ var wheels_default = {
20406
20596
  patches: []
20407
20597
  }
20408
20598
  },
20599
+ {
20600
+ name: "simplejson",
20601
+ version: "4.1.2",
20602
+ filename: "simplejson-4.1.2-cp313-cp313-emscripten_5_0_6_wasm32.whl",
20603
+ sha256: "80671c2b02beb0d429108df8cefb6f27857b34e032bb5a5f1e0f957e7bf3cab6",
20604
+ abiId: "sbxabi1-c2637d04695ad927",
20605
+ requires: [],
20606
+ provenance: {
20607
+ sourceTreeDigest: "055af71755ff30d71076fdebc6f605e66f5876e6f01350a7d6075e8679006111",
20608
+ recipeSha256: "e163a1f2e78f32265c3ffdbf743dab27bc9e0829f21c553cabf0bafafd7d69fd",
20609
+ buildTools: [
20610
+ "setuptools==84.0.0"
20611
+ ],
20612
+ nativeDependencies: [],
20613
+ patches: []
20614
+ }
20615
+ },
20409
20616
  {
20410
20617
  name: "siphash24",
20411
20618
  version: "1.9",
@@ -20429,6 +20636,44 @@ var wheels_default = {
20429
20636
  ]
20430
20637
  }
20431
20638
  },
20639
+ {
20640
+ name: "tiktoken",
20641
+ version: "0.14.0",
20642
+ filename: "tiktoken-0.14.0-cp313-cp313-emscripten_5_0_6_wasm32.whl",
20643
+ sha256: "a006e3c7d5281b05af21d11692d9be00bb2d4ca554589889fe93c5616e94d02f",
20644
+ abiId: "sbxabi1-c2637d04695ad927",
20645
+ requires: [
20646
+ "regex>=2022.1.18",
20647
+ "requests>=2.26.0"
20648
+ ],
20649
+ provenance: {
20650
+ sourceTreeDigest: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
20651
+ recipeSha256: "99b8a588584b366576b343a3eeeff8cae87f9ba917b26460b050b2fc462239ed",
20652
+ buildTools: [
20653
+ "setuptools==84.0.0"
20654
+ ],
20655
+ nativeDependencies: [],
20656
+ patches: []
20657
+ }
20658
+ },
20659
+ {
20660
+ name: "ujson",
20661
+ version: "6.0.0",
20662
+ filename: "ujson-6.0.0-cp313-cp313-emscripten_5_0_6_wasm32.whl",
20663
+ sha256: "3d4fd009acb9fad7a2070b0e2e498f6257e9c0cc28f806950d8c04cbb6849c24",
20664
+ abiId: "sbxabi1-c2637d04695ad927",
20665
+ requires: [],
20666
+ provenance: {
20667
+ sourceTreeDigest: "2a0c67ecb71628cb89b46090372d11dc4cf6e91c2406260a4e544c17331a31da",
20668
+ recipeSha256: "e9f3fe48e14241bd38d1d20dc691413d8c646984d67ae3898f4b27d10af4bf80",
20669
+ buildTools: [
20670
+ "setuptools==84.0.0",
20671
+ "setuptools-scm==10.2.3"
20672
+ ],
20673
+ nativeDependencies: [],
20674
+ patches: []
20675
+ }
20676
+ },
20432
20677
  {
20433
20678
  name: "uuid-utils",
20434
20679
  version: "0.17.1",
@@ -20511,7 +20756,7 @@ var wheels_default = {
20511
20756
 
20512
20757
  // package.json
20513
20758
  var package_default = {
20514
- version: "0.1.91"};
20759
+ version: "0.1.93"};
20515
20760
 
20516
20761
  // src/python/extension-abi.ts
20517
20762
  var EXTENSION_ABI = {
@@ -22616,7 +22861,13 @@ async function serve(ctx, connection) {
22616
22861
  headers: forwarded,
22617
22862
  ...request.body.length > 0 ? { body: request.body } : {}
22618
22863
  });
22619
- writeResponse(connection, result.status, result.statusText, result.headers, result.body);
22864
+ const {
22865
+ "content-encoding": _encoding,
22866
+ "content-length": _length,
22867
+ "transfer-encoding": _chunked,
22868
+ ...responseHeaders
22869
+ } = result.headers;
22870
+ writeResponse(connection, result.status, result.statusText, responseHeaders, result.body);
22620
22871
  } catch (error) {
22621
22872
  const message = error instanceof Error ? error.message : String(error);
22622
22873
  try {
@@ -22915,16 +23166,33 @@ def run(main, *, loop_factory=new_event_loop, debug=None, **run_kwargs):
22915
23166
  with __asyncio.Runner(loop_factory=loop_factory, debug=debug) as runner:
22916
23167
  return runner.run(main)
22917
23168
  `;
22918
- function builtinDistribution(name, version, summary, files) {
23169
+ var HTTPX2_JSFETCH_VERSION = "1.0";
23170
+ var HTTPX2_JSFETCH_SOURCE = `"""SandboxedJs stand-in for httpx2-jsfetch.
23171
+
23172
+ The real package drives the browser fetch API through Pyodide's \`js\` module.
23173
+ This runtime has no \`js\` module but does have sockets, so httpx2's regular
23174
+ httpcore2 transports are used under the names httpx2 imports on Emscripten.
23175
+ """
23176
+
23177
+ from httpx2._transports.default import (
23178
+ AsyncHTTPTransport as AsyncJavascriptFetchTransport,
23179
+ HTTPTransport as JavascriptFetchTransport,
23180
+ )
23181
+
23182
+ __all__ = ["AsyncJavascriptFetchTransport", "JavascriptFetchTransport"]
23183
+ `;
23184
+ function builtinDistribution(name, version, summary, files, requires = []) {
22919
23185
  return {
22920
23186
  version,
23187
+ requires,
22921
23188
  files: {
22922
23189
  ...files,
22923
23190
  [`${name}-${version}.dist-info/METADATA`]: `Metadata-Version: 2.1
22924
23191
  Name: ${name}
22925
23192
  Version: ${version}
22926
23193
  Summary: ${summary}
22927
- `,
23194
+ ` + requires.map((r) => `Requires-Dist: ${r}
23195
+ `).join(""),
22928
23196
  [`${name}-${version}.dist-info/WHEEL`]: "Wheel-Version: 1.0\nGenerator: sandboxedjs\nRoot-Is-Purelib: true\nTag: py3-none-any\n",
22929
23197
  [`${name}-${version}.dist-info/INSTALLER`]: "sandboxedjs\n",
22930
23198
  [`${name}-${version}.dist-info/RECORD`]: ""
@@ -22932,6 +23200,13 @@ Summary: ${summary}
22932
23200
  };
22933
23201
  }
22934
23202
  var BUILTINS3 = {
23203
+ "httpx2-jsfetch": builtinDistribution(
23204
+ "httpx2_jsfetch",
23205
+ HTTPX2_JSFETCH_VERSION,
23206
+ "SandboxedJs stand-in: httpx2's socket transports under the jsfetch names",
23207
+ { "httpx2_jsfetch/__init__.py": HTTPX2_JSFETCH_SOURCE },
23208
+ ["httpcore2[asyncio]", "anyio>=4.10", "truststore>=0.10"]
23209
+ ),
22935
23210
  greenlet: builtinDistribution(
22936
23211
  "greenlet",
22937
23212
  GREENLET_VERSION,
@@ -22956,7 +23231,7 @@ function builtinCandidates(name) {
22956
23231
  filename: `${name}-${builtin.version}-py3-none-any.whl`,
22957
23232
  sha256: "",
22958
23233
  metadataUrl: null,
22959
- indexedRequires: []
23234
+ indexedRequires: builtin.requires
22960
23235
  }];
22961
23236
  }
22962
23237
  function isBuiltinOnly(name) {
@@ -23852,6 +24127,36 @@ if sys.platform in ("emscripten", "wasi"):
23852
24127
 
23853
24128
  sys.meta_path.insert(0, _SbxAsyncioWatcherHook())
23854
24129
 
24130
+ class _SbxPyodideFetchHook:
24131
+ """Libraries read \`sys.platform == "emscripten"\` as "running in Pyodide"
24132
+ and switch to a browser-fetch backend that imports Pyodide's \`js\`
24133
+ module. This runtime has no \`js\` but does have sockets, so those
24134
+ backends are replaced by modules that leave the socket path in place.
24135
+ urllib3 (and so requests) imports its backend at package import, which
24136
+ made anything using requests fail with "No module named 'js'"."""
24137
+
24138
+ _STANDINS = {
24139
+ # urllib3/__init__.py calls inject_into_urllib3() on emscripten.
24140
+ "urllib3.contrib.emscripten": "def inject_into_urllib3():\\n pass\\n",
24141
+ }
24142
+
24143
+ def find_spec(self, name, path=None, target=None):
24144
+ source = self._STANDINS.get(name)
24145
+ if source is None:
24146
+ return None
24147
+ import importlib.util
24148
+
24149
+ class _Loader:
24150
+ def create_module(self, spec):
24151
+ return None
24152
+
24153
+ def exec_module(self, module):
24154
+ exec(compile(source, f"<sandboxedjs {name}>", "exec"), module.__dict__)
24155
+
24156
+ return importlib.util.spec_from_loader(name, _Loader(), is_package=False)
24157
+
24158
+ sys.meta_path.insert(0, _SbxPyodideFetchHook())
24159
+
23855
24160
 
23856
24161
  _egress = os.environ.get("SBX_HTTP_EGRESS")
23857
24162
  if _egress:
@@ -23933,6 +24238,255 @@ if _egress:
23933
24238
  http.client.HTTPConnection = _SbxEgressConnection
23934
24239
  http.client.HTTPSConnection = _SbxEgressTlsConnection
23935
24240
 
24241
+ # Everything above routes code that reaches for the standard library. The
24242
+ # popular HTTP clients do not: urllib3 (so requests), httpcore (so httpx)
24243
+ # and httpcore2 (so httpx2) open sockets themselves, and raw sockets do not
24244
+ # leave this container -- connect() to anything but loopback is
24245
+ # ENETUNREACH, and CPython's ssl cannot wrap these sockets anyway. So each
24246
+ # client's connection layer is replaced by one that speaks plain HTTP to
24247
+ # the egress endpoint with the absolute URL in the request line, which is
24248
+ # what the standard-library shims above already do.
24249
+
24250
+ def _sbx_egress_request(method, url, headers, body, timeout=None):
24251
+ """One request through the egress. Returns (status, reason, headers, body)."""
24252
+ connection = _RealHTTPConnection(_egress_host, _egress_port, timeout=timeout)
24253
+ try:
24254
+ connection.request(method, url, body, {k: v for k, v in headers if k.lower() != "host"})
24255
+ response = connection.getresponse()
24256
+ payload = response.read()
24257
+ if response.getheader("x-sbx-error"):
24258
+ raise OSError(payload.decode("utf-8", "replace").strip())
24259
+ return response.status, response.reason, response.getheaders(), payload
24260
+ finally:
24261
+ connection.close()
24262
+
24263
+ async def _sbx_egress_request_async(method, url, headers, body):
24264
+ """The same hop as _sbx_egress_request, over asyncio streams."""
24265
+ import asyncio
24266
+ reader, writer = await asyncio.open_connection(_egress_host, _egress_port)
24267
+ try:
24268
+ payload = body or b""
24269
+ if isinstance(payload, str):
24270
+ payload = payload.encode()
24271
+ lines = [f"{method} {url} HTTP/1.1", f"Host: {_egress_host}:{_egress_port}"]
24272
+ for key, value in headers:
24273
+ if key.lower() in ("host", "connection", "content-length", "transfer-encoding"):
24274
+ continue
24275
+ lines.append(f"{key}: {value}")
24276
+ lines.append(f"Content-Length: {len(payload)}")
24277
+ # Read to EOF rather than tracking keep-alive framing: one request
24278
+ # per connection, which is what this hop is for.
24279
+ lines.append("Connection: close")
24280
+ writer.write(("\\r\\n".join(lines) + "\\r\\n\\r\\n").encode("latin-1") + payload)
24281
+ await writer.drain()
24282
+
24283
+ head = await reader.readuntil(b"\\r\\n\\r\\n")
24284
+ start, _, rest = head.decode("latin-1").partition("\\r\\n")
24285
+ _, _, status_text = start.partition(" ")
24286
+ code, _, reason = status_text.partition(" ")
24287
+ received = []
24288
+ for line in rest.split("\\r\\n"):
24289
+ key, sep, value = line.partition(":")
24290
+ if sep:
24291
+ received.append((key.strip(), value.strip()))
24292
+ content = dict((k.lower(), v) for k, v in received)
24293
+ if "content-length" in content:
24294
+ data = await reader.readexactly(int(content["content-length"]))
24295
+ else:
24296
+ data = await reader.read()
24297
+ if content.get("x-sbx-error"):
24298
+ raise OSError(data.decode("utf-8", "replace").strip())
24299
+ return int(code), reason, received, data
24300
+ finally:
24301
+ writer.close()
24302
+
24303
+ class _SbxUrllib3Hook:
24304
+ """urllib3's emscripten backend is Pyodide's \`js\` fetch, which this
24305
+ runtime has no module for; its socket backend cannot leave the
24306
+ container. Both connection classes become egress ones."""
24307
+
24308
+ def find_spec(self, name, path=None, target=None):
24309
+ if name != "urllib3.contrib.emscripten":
24310
+ return None
24311
+ import importlib.util
24312
+
24313
+ def inject_into_urllib3():
24314
+ import urllib3.connection
24315
+ from urllib3.connectionpool import HTTPConnectionPool, HTTPSConnectionPool
24316
+
24317
+ class _Plain(urllib3.connection.HTTPConnection):
24318
+ """urllib3's own connection, dialled at the egress.
24319
+
24320
+ urllib3.connection.HTTPConnection still derives from
24321
+ http.client.HTTPConnection, so request building is
24322
+ inherited; only where it connects and what goes in the
24323
+ request line change.
24324
+ """
24325
+
24326
+ _sbx_scheme = "http"
24327
+ default_port = 80
24328
+ is_verified = True
24329
+ proxy_is_verified = None
24330
+
24331
+ def __init__(self, host, port=None, **kwargs):
24332
+ # A distinct name from the http.client shim's
24333
+ # _sbx_origin: that class is further up this MRO and
24334
+ # its __init__ runs after this one, so sharing the name
24335
+ # overwrote the real destination with the egress and
24336
+ # every request went out addressed to the egress itself.
24337
+ self._sbx_target = (host, port or self.default_port)
24338
+ for unsupported in ("ssl_context", "ssl_version", "ssl_minimum_version",
24339
+ "ssl_maximum_version", "assert_hostname",
24340
+ "assert_fingerprint", "server_hostname", "ca_certs",
24341
+ "ca_cert_dir", "ca_cert_data", "cert_file", "key_file",
24342
+ "key_password", "cert_reqs"):
24343
+ kwargs.pop(unsupported, None)
24344
+ super().__init__(_egress_host, port=_egress_port, **kwargs)
24345
+
24346
+ def _sbx_absolute(self, url):
24347
+ host, port = self._sbx_target
24348
+ if url.startswith("http://") or url.startswith("https://"):
24349
+ return url
24350
+ netloc = host if port == self.default_port else f"{host}:{port}"
24351
+ return f"{self._sbx_scheme}://{netloc}{url}"
24352
+
24353
+ # urllib3 v2 writes the request line in its own request()
24354
+ # rather than through http.client's putrequest, so both
24355
+ # are absolutised; the egress refuses an origin-form target.
24356
+ def request(self, method, url, *args, **kwargs):
24357
+ return super().request(method, self._sbx_absolute(url), *args, **kwargs)
24358
+
24359
+ def putrequest(self, method, url, *args, **kwargs):
24360
+ return super().putrequest(method, self._sbx_absolute(url), *args, **kwargs)
24361
+
24362
+ def set_tunnel(self, *args, **kwargs):
24363
+ raise OSError("this container reaches the network through its egress, not CONNECT")
24364
+
24365
+ class _Tls(_Plain):
24366
+ _sbx_scheme = "https"
24367
+ default_port = 443
24368
+
24369
+ HTTPConnectionPool.ConnectionCls = _Plain
24370
+ HTTPSConnectionPool.ConnectionCls = _Tls
24371
+ urllib3.connection.HTTPConnection = _Plain
24372
+ urllib3.connection.HTTPSConnection = _Tls
24373
+
24374
+ class _Loader:
24375
+ def create_module(self, spec):
24376
+ return None
24377
+
24378
+ def exec_module(self, module):
24379
+ module.inject_into_urllib3 = inject_into_urllib3
24380
+
24381
+ return importlib.util.spec_from_loader(name, _Loader(), is_package=True)
24382
+
24383
+ sys.meta_path.insert(0, _SbxUrllib3Hook())
24384
+
24385
+ def _sbx_httpx_transports(httpx_module):
24386
+ """Sync and async httpx transports that send through the egress."""
24387
+
24388
+ class _Transport(httpx_module.BaseTransport):
24389
+ def __init__(self, *args, **kwargs):
24390
+ pass
24391
+
24392
+ def handle_request(self, request):
24393
+ body = request.read()
24394
+ sent = [(k.decode("latin-1"), v.decode("latin-1")) for k, v in request.headers.raw]
24395
+ status, reason, headers, payload = _sbx_egress_request(
24396
+ request.method, str(request.url), sent, body,
24397
+ )
24398
+ # Content-Length/Transfer-Encoding describe the egress hop's
24399
+ # framing, and httpx re-derives them from the body it is given.
24400
+ keep = [(k, v) for k, v in headers
24401
+ if k.lower() not in ("content-length", "transfer-encoding", "content-encoding")]
24402
+ return httpx_module.Response(
24403
+ status, headers=keep, content=payload, request=request,
24404
+ extensions={"reason_phrase": (reason or "").encode()},
24405
+ )
24406
+
24407
+ def close(self):
24408
+ pass
24409
+
24410
+ def __enter__(self):
24411
+ return self
24412
+
24413
+ def __exit__(self, *args):
24414
+ pass
24415
+
24416
+ class _AsyncTransport(httpx_module.AsyncBaseTransport):
24417
+ def __init__(self, *args, **kwargs):
24418
+ pass
24419
+
24420
+ async def handle_async_request(self, request):
24421
+ # Not the blocking client on the loop thread: this image
24422
+ # proxies every syscall to one thread, so a blocking recv
24423
+ # inside a coroutine stops the loop that must service it. The
24424
+ # egress is loopback, which asyncio streams reach.
24425
+ import asyncio
24426
+ body = await request.aread()
24427
+ sent = [(k.decode("latin-1"), v.decode("latin-1")) for k, v in request.headers.raw]
24428
+ status, reason, headers, payload = await _sbx_egress_request_async(
24429
+ request.method, str(request.url), sent, body,
24430
+ )
24431
+ keep = [(k, v) for k, v in headers
24432
+ if k.lower() not in ("content-length", "transfer-encoding", "content-encoding")]
24433
+ return httpx_module.Response(
24434
+ status, headers=keep, content=payload, request=request,
24435
+ extensions={"reason_phrase": (reason or "").encode()},
24436
+ )
24437
+
24438
+ async def aclose(self):
24439
+ pass
24440
+
24441
+ async def __aenter__(self):
24442
+ return self
24443
+
24444
+ async def __aexit__(self, *args):
24445
+ pass
24446
+
24447
+ return _Transport, _AsyncTransport
24448
+
24449
+ class _SbxHttpxHook:
24450
+ """httpx and httpx2 build their default transports on httpcore, which
24451
+ opens sockets. Their default transport classes are replaced as the
24452
+ module loads, so \`httpx.get(...)\` and every client built from the
24453
+ defaults leave through the egress."""
24454
+
24455
+ _PACKAGES = {"httpx": "httpx", "httpx2": "httpx2"}
24456
+
24457
+ def find_spec(self, name, path=None, target=None):
24458
+ package = self._PACKAGES.get(name)
24459
+ if package is None or getattr(self, "_loading", None) == name:
24460
+ return None
24461
+ import importlib.machinery
24462
+ spec = importlib.machinery.PathFinder.find_spec(name, path)
24463
+ if spec is None or spec.loader is None:
24464
+ return spec
24465
+ load = spec.loader.exec_module
24466
+
24467
+ def exec_module(module):
24468
+ self._loading = name
24469
+ try:
24470
+ load(module)
24471
+ finally:
24472
+ self._loading = None
24473
+ transport, async_transport = _sbx_httpx_transports(module)
24474
+ module.HTTPTransport = transport
24475
+ module.AsyncHTTPTransport = async_transport
24476
+ defaults = sys.modules.get(f"{package}._transports.default")
24477
+ if defaults is not None:
24478
+ defaults.HTTPTransport = transport
24479
+ defaults.AsyncHTTPTransport = async_transport
24480
+ client = sys.modules.get(f"{package}._client")
24481
+ if client is not None:
24482
+ client.HTTPTransport = transport
24483
+ client.AsyncHTTPTransport = async_transport
24484
+
24485
+ spec.loader.exec_module = exec_module
24486
+ return spec
24487
+
24488
+ sys.meta_path.insert(0, _SbxHttpxHook())
24489
+
23936
24490
 
23937
24491
  if os.environ.get("SBX_SERIAL_HOST_CALLS") == "1":
23938
24492
  import asyncio
@@ -23961,6 +24515,47 @@ if os.environ.get("SBX_SERIAL_HOST_CALLS") == "1":
23961
24515
  )
23962
24516
 
23963
24517
  asyncio.to_thread = _to_thread
24518
+
24519
+ class _SbxAnyioThreadHook:
24520
+ """anyio's thread offload, run inline.
24521
+
24522
+ anyio has its own worker threads, so it never reaches asyncio.to_thread
24523
+ and the refusal above -- it deadlocks instead, for the reason described
24524
+ there. Starlette sends every *synchronous* endpoint through
24525
+ anyio.to_thread.run_sync, so a plain def FastAPI route hung forever
24526
+ while an async def one answered.
24527
+
24528
+ Running the function on the loop thread keeps such an endpoint working.
24529
+ The cost is real and worth stating: a synchronous handler blocks the
24530
+ loop for its duration, so it does not overlap with other requests.
24531
+ """
24532
+
24533
+ def find_spec(self, name, path=None, target=None):
24534
+ if name != "anyio.to_thread":
24535
+ return None
24536
+ import importlib.machinery
24537
+ spec = importlib.machinery.PathFinder.find_spec(name, path)
24538
+ if spec is None or spec.loader is None:
24539
+ return spec
24540
+ load = spec.loader.exec_module
24541
+
24542
+ def exec_module(module):
24543
+ load(module)
24544
+
24545
+ async def run_sync(func, *args, abandon_on_cancel=False,
24546
+ cancellable=None, limiter=None):
24547
+ return func(*args)
24548
+
24549
+ async def run_sync_in_worker_thread(func, *args, **kwargs):
24550
+ return func(*args)
24551
+
24552
+ module.run_sync = run_sync
24553
+ module.run_sync_in_worker_thread = run_sync_in_worker_thread
24554
+
24555
+ spec.loader.exec_module = exec_module
24556
+ return spec
24557
+
24558
+ sys.meta_path.insert(0, _SbxAnyioThreadHook())
23964
24559
  `;
23965
24560
  function ensureProcessShims(ctx) {
23966
24561
  const hook = `${SITE_PACKAGES}/sitecustomize.py`;