Full Code of weavejester/haslett for AI

master 83c7d5e3e70d cached
11 files
11.6 KB
3.6k tokens
1 requests
Download .txt
Repository: weavejester/haslett
Branch: master
Commit: 83c7d5e3e70d
Files: 11
Total size: 11.6 KB

Directory structure:
gitextract_bh_f4pej/

├── .github/
│   └── workflows/
│       └── test.yml
├── .gitignore
├── .travis.yml
├── README.md
├── package.json
├── project.clj
├── src/
│   └── haslett/
│       ├── client.cljs
│       └── format.cljs
└── test/
    └── haslett/
        ├── client_test.cljs
        ├── test_runner.clj
        └── test_runner.cljs

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/test.yml
================================================
name: Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Prepare java
        uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: '8'

      - name: Prepare node
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install NPM dependencies
        run: npm ci

      - name: Install clojure tools
        uses: DeLaGuardo/setup-clojure@12.5
        with:
          lein: 2.9.10

      - name: Cache clojure dependencies
        uses: actions/cache@v4
        with:
          path: ~/.m2/repository
          key: cljdeps-${{ hashFiles('project.clj') }}
          restore-keys: cljdeps-

      - name: Run tests
        run: lein test


================================================
FILE: .gitignore
================================================
/target
/classes
/checkouts
pom.xml
pom.xml.asc
*.jar
*.class
/.lein-*
/.nrepl-port
.hgignore
.hg/
/node_modules
/.clj-kondo


================================================
FILE: .travis.yml
================================================
language: clojure
dist: trusty
addons:
  chrome: stable
install:
  - npm install karma karma-cljs-test --save-dev
  - npm install -g karma-cli
  - npm install karma-chrome-launcher --save-dev
script:
  - lein test


================================================
FILE: README.md
================================================
# Haslett [![Build Status](https://github.com/weavejester/haslett/actions/workflows/test.yml/badge.svg)](https://github.com/weavejester/haslett/actions/workflows/test.yml)

A lightweight WebSocket library for ClojureScript that uses
[core.async][].

[core.async]: https://github.com/clojure/core.async

## Installation

Add the following dependency to your deps.edn file:

    haslett/haslett {:mvn/version "0.2.0"}

Or to your Leiningen project file:

    [haslett "0.2.0"]

## Usage

Haslett provides a simple and idiomatic interface to using WebSockets:

```clojure
(ns example.core
  (:require [cljs.core.async :as a :refer [<! >! go]]
            [haslett.client :as ws]
            [haslett.format :as fmt]))

(go (let [stream (<! (ws/connect "ws://echo.websocket.org"))]
      (>! (:out stream) "Hello World")
      (js/console.log (<! (:in stream)))
      (ws/close stream)))
```

The `connect` function returns a promise channel that produces a map
with four keys: `:socket`, `:close-status`, `:in` and `:out`.

* `:socket` contains the JavaScript `WebSocket` object, in case you need
to access it directly.

* `:close-status` contains a promise channel that a status map is
delivered to when the socket is closed. The status map will provide a
`:code` and `:reason` keys that will explain why the socket was
closed.

* `:in` is a core.async channel to read from.

* `:out` is a core.async channel to write to.

By default, Haslett sends raw strings, but we can change that by
supplying a formatter. Haslett includes formatters for JSON, edn and
Transit:

```clojure
(go (let [stream (<! (ws/connect "ws://echo.websocket.org" {:format fmt/transit}))]
      (>! (:out stream) {:foo [1 2 3]})
      (js/console.log (pr-str (<! (:in stream))))
      (ws/close stream)))
```

You can customize the behaviour further by supplying your own channels
for the source and sink. This allows you to tune the channel buffer,
and add transducers:

```clojure
(ws/connect "ws://echo.websocket.org"
            {:in  (a/chan 10)
             :out (a/chan 10)})
```

When the WebSocket is closed, the `:out` and `:in` channels are
also closed. In addition, a final status map will be delivered to a
promise channel held in the `:close-status` key on the stream.

## License

Copyright © 2024 James Reeves

Distributed under the Eclipse Public License either version 1.0 or (at
your option) any later version.


================================================
FILE: package.json
================================================
{
  "devDependencies": {
    "karma": "^6.4.3",
    "karma-cljs-test": "^0.1.0",
    "karma-firefox-launcher": "^2.1.3"
  }
}


================================================
FILE: project.clj
================================================
(defproject haslett "0.2.0"
  :description "A lightweight WebSocket library for ClojureScript"
  :url "https://github.com/weavejester/haslett"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.11.3"]
                 [org.clojure/clojurescript "1.11.132"]
                 [org.clojure/core.async "1.6.681"]
                 [com.cognitect/transit-cljs "0.8.280"]]
  :plugins [[lein-cljsbuild "1.1.8"]]
  :aliases {"test" ["run" "-m" "haslett.test-runner"]}
  :profiles
  {:dev {:dependencies [[doo "0.1.11"]
                        [http-kit "2.8.0"]]
         :prep-tasks   ["compile" ["cljsbuild" "once"]]
         :cljsbuild
         {:builds
          {:test
           {:source-paths ["src" "test"]
            :compiler {:output-to "target/main.js"
                       :output-dir "target"
                       :main haslett.test-runner
                       :optimizations :none}}}}}})


================================================
FILE: src/haslett/client.cljs
================================================
(ns haslett.client
  "A namespace for opening WebSockets in ClojureScript."
  (:require [cljs.core.async :as a :refer [<! go-loop]]
            [haslett.format :as fmt]))

(defn close
  "Close a stream opened by connect."
  [stream]
  (.close (:socket stream) 1000 "Closed by creator")
  (:close-status stream))

(defn connect
  "Create a WebSocket to the specified URL, and returns a 'stream' map of four
  keys:

    :socket       - contains the WebSocket object
    :close-status - a promise channel that contains the final close status
    :in           - a core.async channel to read from
    :out          - a core.async channel to write to

  Takes the following options:

    :format      - a formatter from haslett.format
    :in          - a custom channel to use as the reader
    :out         - a custom channel to use as the writer
    :protocols   - passed to the WebSocket, a vector of protocol strings
    :binary-type - passed to the WebSocket, may be :blob or :arraybuffer
    :close-chan? - true if channels should be closed if WebSocket is closed
                   (defaults to true)

  The WebSocket may either be closed directly, or by closing the
  stream's :sink channel."
  ([url]
   (connect url {}))
  ([url options]
   (let [protocols (into-array (:protocols options []))
         socket    (js/WebSocket. url protocols)
         in        (:in options (a/chan))
         out       (:out options (a/chan))
         format    (:format options fmt/identity)
         status    (a/promise-chan)
         return    (a/promise-chan)
         close?    (:close-chan? options true)
         stream    {:socket socket, :in in, :out out, :close-status status}]
     (set! (.-binaryType socket) (name (:binary-type options :arraybuffer)))
     (set! (.-onopen socket)     (fn [_] (a/put! return stream)))
     (set! (.-onmessage socket)
           (fn [e] (a/put! in (fmt/read format (.-data e)))))
     (set! (.-onclose socket)
           (fn [e]
             (a/put! status {:reason (.-reason e), :code (.-code e)})
             (when close? (a/close! in))
             (when close? (a/close! out))
             (a/put! return stream)))
     (go-loop []
       (when-let [msg (<! out)]
         (.send socket (fmt/write format msg))
         (recur))
       (close stream))
     return)))

(defn connected?
  "Return true if the stream is currently connected."
  [{:keys [close-status]}]
  (nil? (a/poll! close-status)))


================================================
FILE: src/haslett/format.cljs
================================================
(ns haslett.format
  "A namespace containing formatters that read and write information from
  WebSocket streams. Used with haslett.client/connect."
  (:refer-clojure :exclude [identity])
  (:require [cljs.reader :as edn]
            [cognitect.transit :as transit]))

(defprotocol Format
  "The format protocol."
  (read  [formatter string])
  (write [formatter value]))

(def identity
  "The identity formatter. Does nothing to the input or output."
  (reify Format
    (read  [_ s] s)
    (write [_ v] v)))

(def transit
  "Read and write data encoded in transit+json."
  (reify Format
    (read  [_ s] (transit/read (transit/reader :json) s))
    (write [_ v] (transit/write (transit/writer :json) v))))

(def edn
  "Read and write data encoded in edn."
  (reify Format
    (read  [_ s] (edn/read-string s))
    (write [_ v] (pr-str v))))

(def json
  "Read and write data encoded in JSON."
  (reify Format
    (read  [_ s] (js->clj (js/JSON.parse s)))
    (write [_ v] (js/JSON.stringify (clj->js v)))))


================================================
FILE: test/haslett/client_test.cljs
================================================
(ns haslett.client-test
  (:require [cljs.test :refer-macros [deftest is async]]
            [cljs.core.async :as a :refer [<! >! go]]
            [cljs.core.async.impl.protocols :as ap]
            [haslett.client :as ws]
            [haslett.format :as fmt]))

(deftest test-defaults
  (async done
    (go (let [stream (<! (ws/connect "ws://localhost:3200"))]
          (is (ws/connected? stream))
          (>! (:out stream) "Hello World")
          (is (= (<! (:in stream)) "Hello World"))
          (ws/close stream)
          (done)))))

(deftest test-transit
  (async done
    (go (let [stream (<! (ws/connect "ws://localhost:3200"
                                     {:format fmt/transit}))]
          (>! (:out stream) {:hello "World"})
          (is (= (<! (:in stream)) {:hello "World"}))
          (ws/close stream)
          (done)))))

(deftest test-edn
  (async done
    (go (let [stream (<! (ws/connect "ws://localhost:3200" {:format fmt/edn}))]
          (>! (:out stream) {:hello "World"})
          (is (= (<! (:in stream)) {:hello "World"}))
          (ws/close stream)
          (done)))))

(deftest test-json
  (async done
    (go (let [stream (<! (ws/connect "ws://localhost:3200" {:format fmt/json}))]
          (>! (:out stream) {:hello "World"})
          (is (= (<! (:in stream)) {"hello" "World"}))
          (ws/close stream)
          (done)))))

(deftest test-close
  (async done
    (go (let [stream (<! (ws/connect "ws://localhost:3200"))]
          (is (= (<! (ws/close stream))
                 {:code 1000, :reason "Closed by creator"}))
          (is (= (<! (:close-status stream))
                 {:code 1000, :reason "Closed by creator"}))
          (done)))))

(deftest test-connection-fail
  (async done
    (go (let [stream (<! (ws/connect "ws://localhost:3201"))]
          (is (not (ws/connected? stream)))
          (is (ap/closed? (:out stream)))
          (is (ap/closed? (:in stream)))
          (is (= (<! (:close-status stream)) {:code 1006, :reason ""}))
          (done)))))

(deftest test-local-close
  (async done
    (go (let [stream (<! (ws/connect "ws://localhost:3200"))]
          (a/close! (:out stream))
          (is (= (<! (:close-status stream))
                 {:code 1000, :reason "Closed by creator"}))
          (is (ap/closed? (:in stream)))
          (done)))))

(deftest test-chans-not-closed
  (async done
    (go (let [stream (<! (ws/connect "ws://localhost:3200"
                                     {:close-chan? false}))]
          (ws/close stream)
          (is (= (<! (:close-status stream))
                 {:code 1000, :reason "Closed by creator"}))
          (is (not (ap/closed? (:out stream))))
          (is (not (ap/closed? (:in stream))))
          (done)))))


================================================
FILE: test/haslett/test_runner.clj
================================================
(ns haslett.test-runner
  (:require [doo.core :as doo]
            [org.httpkit.server :as httpkit]))

(def doo-opts
  {:paths {:karma "node_modules/karma/bin/karma"}})

(def compiler-opts
  {:output-to "target/main.js"
   :output-dir "target"
   :main 'haslett.test-runner
   :optimizations :none})

(defn echo-handler [request]
  (httpkit/with-channel request channel
    (httpkit/on-receive channel (fn [data] (httpkit/send! channel data)))))

(defn run-server []
  (httpkit/run-server echo-handler {:port 3200}))

(defn run-tests []
  (doo/run-script :firefox-headless compiler-opts doo-opts))

(defn -main []
  (println "Starting server")
  (let [stop-server (run-server)]
    (println "Running tests")
    (run-tests)
    (println "Stopping server")
    (stop-server)
    (shutdown-agents)))


================================================
FILE: test/haslett/test_runner.cljs
================================================
(ns haslett.test-runner
  (:require [doo.runner :refer-macros [doo-tests]]
            [haslett.client-test]))

(doo-tests 'haslett.client-test)
Download .txt
gitextract_bh_f4pej/

├── .github/
│   └── workflows/
│       └── test.yml
├── .gitignore
├── .travis.yml
├── README.md
├── package.json
├── project.clj
├── src/
│   └── haslett/
│       ├── client.cljs
│       └── format.cljs
└── test/
    └── haslett/
        ├── client_test.cljs
        ├── test_runner.clj
        └── test_runner.cljs
Condensed preview — 11 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (13K chars).
[
  {
    "path": ".github/workflows/test.yml",
    "chars": 845,
    "preview": "name: Tests\non: [push, pull_request]\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        "
  },
  {
    "path": ".gitignore",
    "chars": 125,
    "preview": "/target\n/classes\n/checkouts\npom.xml\npom.xml.asc\n*.jar\n*.class\n/.lein-*\n/.nrepl-port\n.hgignore\n.hg/\n/node_modules\n/.clj-k"
  },
  {
    "path": ".travis.yml",
    "chars": 214,
    "preview": "language: clojure\ndist: trusty\naddons:\n  chrome: stable\ninstall:\n  - npm install karma karma-cljs-test --save-dev\n  - np"
  },
  {
    "path": "README.md",
    "chars": 2401,
    "preview": "# Haslett [![Build Status](https://github.com/weavejester/haslett/actions/workflows/test.yml/badge.svg)](https://github."
  },
  {
    "path": "package.json",
    "chars": 126,
    "preview": "{\n  \"devDependencies\": {\n    \"karma\": \"^6.4.3\",\n    \"karma-cljs-test\": \"^0.1.0\",\n    \"karma-firefox-launcher\": \"^2.1.3\"\n"
  },
  {
    "path": "project.clj",
    "chars": 996,
    "preview": "(defproject haslett \"0.2.0\"\n  :description \"A lightweight WebSocket library for ClojureScript\"\n  :url \"https://github.co"
  },
  {
    "path": "src/haslett/client.cljs",
    "chars": 2442,
    "preview": "(ns haslett.client\n  \"A namespace for opening WebSockets in ClojureScript.\"\n  (:require [cljs.core.async :as a :refer [<"
  },
  {
    "path": "src/haslett/format.cljs",
    "chars": 1009,
    "preview": "(ns haslett.format\n  \"A namespace containing formatters that read and write information from\n  WebSocket streams. Used w"
  },
  {
    "path": "test/haslett/client_test.cljs",
    "chars": 2751,
    "preview": "(ns haslett.client-test\n  (:require [cljs.test :refer-macros [deftest is async]]\n            [cljs.core.async :as a :ref"
  },
  {
    "path": "test/haslett/test_runner.clj",
    "chars": 798,
    "preview": "(ns haslett.test-runner\n  (:require [doo.core :as doo]\n            [org.httpkit.server :as httpkit]))\n\n(def doo-opts\n  {"
  },
  {
    "path": "test/haslett/test_runner.cljs",
    "chars": 145,
    "preview": "(ns haslett.test-runner\n  (:require [doo.runner :refer-macros [doo-tests]]\n            [haslett.client-test]))\n\n(doo-tes"
  }
]

About this extraction

This page contains the full source code of the weavejester/haslett GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 11 files (11.6 KB), approximately 3.6k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!