{"id":16851745,"url":"https://github.com/they4kman/dcrf-client","last_synced_at":"2025-09-20T02:56:51.664Z","repository":{"id":34306210,"uuid":"176833693","full_name":"theY4Kman/dcrf-client","owner":"theY4Kman","description":"Javascript client for Django Channels REST Framework","archived":false,"fork":false,"pushed_at":"2023-12-10T03:58:35.000Z","size":413,"stargazers_count":46,"open_issues_count":12,"forks_count":12,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-08-25T21:36:39.129Z","etag":null,"topics":["channels","django","javascript","websocket"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/dcrf-client","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/theY4Kman.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-03-20T23:40:42.000Z","updated_at":"2024-12-05T10:10:30.000Z","dependencies_parsed_at":"2024-06-12T23:31:56.456Z","dependency_job_id":null,"html_url":"https://github.com/theY4Kman/dcrf-client","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/theY4Kman/dcrf-client","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theY4Kman%2Fdcrf-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theY4Kman%2Fdcrf-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theY4Kman%2Fdcrf-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theY4Kman%2Fdcrf-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/theY4Kman","download_url":"https://codeload.github.com/theY4Kman/dcrf-client/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theY4Kman%2Fdcrf-client/sbom","scorecard":{"id":877610,"data":{"date":"2025-08-11","repo":{"name":"github.com/theY4Kman/dcrf-client","commit":"a5d000a846b5cdf4bf6cad5be3733537bf875f67"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.4,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 1/14 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/integration.yml:1","Warn: no topLevel permission defined: .github/workflows/mocha.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":4,"reason":"dependency not pinned by hash detected -- score normalized to 4","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/integration.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/theY4Kman/dcrf-client/integration.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/integration.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/theY4Kman/dcrf-client/integration.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/integration.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/theY4Kman/dcrf-client/integration.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/integration.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/theY4Kman/dcrf-client/integration.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/mocha.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/theY4Kman/dcrf-client/mocha.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/mocha.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/theY4Kman/dcrf-client/mocha.yml/master?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/integration.yml:22","Info:   0 out of   5 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of   1 pipCommand dependencies pinned","Info:   2 out of   2 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 19 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"57 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-3ww4-gg4f-jr7f","Warn: Project is vulnerable to: GHSA-5cpq-8wj7-hf2v","Warn: Project is vulnerable to: GHSA-9v9h-cgj8-h64p","Warn: Project is vulnerable to: PYSEC-2023-254 / GHSA-jfhm-5ghh-2f97","Warn: Project is vulnerable to: GHSA-jm77-qphf-c4w8","Warn: Project is vulnerable to: GHSA-v8gr-m533-ghj9","Warn: Project is vulnerable to: GHSA-w7pp-m8wf-vj6r","Warn: Project is vulnerable to: GHSA-x4qr-2fvf-3mr5","Warn: Project is vulnerable to: PYSEC-2022-190 / GHSA-2gwj-7jmv-h26r","Warn: Project is vulnerable to: PYSEC-2023-13 / GHSA-2hrw-hx67-34x6","Warn: Project is vulnerable to: PYSEC-2022-1 / GHSA-53qw-q765-4fww","Warn: Project is vulnerable to: PYSEC-2022-20 / GHSA-6cw3-g6wv-c2xv","Warn: Project is vulnerable to: PYSEC-2023-225 / GHSA-7h4p-27mh-hmrw","Warn: Project is vulnerable to: GHSA-7xr5-9hcq-chf9","Warn: Project is vulnerable to: PYSEC-2022-2 / GHSA-8c5j-9r9f-c6w8","Warn: Project is vulnerable to: PYSEC-2022-245 / GHSA-8x94-hmjh-97hq","Warn: Project is vulnerable to: PYSEC-2022-19 / GHSA-95rw-fx8r-36v6","Warn: Project is vulnerable to: PYSEC-2023-226 / GHSA-h8gc-pgj2-vjm3","Warn: Project is vulnerable to: PYSEC-2023-100 / GHSA-jh3w-4vvf-mjgr","Warn: Project is vulnerable to: PYSEC-2022-3 / GHSA-jrh2-hc4r-7jwx","Warn: Project is vulnerable to: PYSEC-2022-213 / GHSA-p64x-8rxx-wf6q","Warn: Project is vulnerable to: PYSEC-2023-12 / GHSA-q2jf-h9jm-m7p4","Warn: Project is vulnerable to: PYSEC-2023-222 / GHSA-qmf9-6jqf-j8fq","Warn: Project is vulnerable to: PYSEC-2022-304 / GHSA-qrw5-5h28-6cmg","Warn: Project is vulnerable to: PYSEC-2023-61 / GHSA-r3xc-prgr-mg9p","Warn: Project is vulnerable to: GHSA-rrqc-c2jx-6jgv","Warn: Project is vulnerable to: PYSEC-2021-439 / GHSA-v6rh-hp5x-86rv","Warn: Project is vulnerable to: PYSEC-2024-47 / GHSA-vm8q-m57g-pff3","Warn: Project is vulnerable to: PYSEC-2022-191 / GHSA-w24h-v9qh-8gxj","Warn: Project is vulnerable to: PYSEC-2024-28 / GHSA-xxj9-f6rv-m3x4","Warn: Project is vulnerable to: GHSA-gw84-84pc-xp82","Warn: Project is vulnerable to: PYSEC-2024-60 / GHSA-jjg7-2v4v-x38h","Warn: Project is vulnerable to: PYSEC-2022-42969","Warn: Project is vulnerable to: GHSA-2m57-hf25-phgg","Warn: Project is vulnerable to: PYSEC-2023-87 / GHSA-rrm6-wvj7-cwh2","Warn: Project is vulnerable to: PYSEC-2022-27 / GHSA-92x2-jw7w-xvvx","Warn: Project is vulnerable to: PYSEC-2022-195 / GHSA-c2jg-hw38-jrqq","Warn: Project is vulnerable to: GHSA-c8m8-j448-xjx7","Warn: Project is vulnerable to: PYSEC-2024-75 / GHSA-cf56-g6w6-pqq2","Warn: Project is vulnerable to: PYSEC-2022-160 / GHSA-rv6r-3f5q-9rgx","Warn: Project is vulnerable to: GHSA-vg46-2rrj-3647","Warn: Project is vulnerable to: PYSEC-2023-224 / GHSA-xc8x-vp79-p3wm","Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-fwr7-v2mv-hh25","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-qrpm-p2h7-hrv2","Warn: Project is vulnerable to: GHSA-mwcw-c2x4-8c55","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-24T06:39:50.276Z","repository_id":34306210,"created_at":"2025-08-24T06:39:50.276Z","updated_at":"2025-08-24T06:39:50.276Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":276038056,"owners_count":25574251,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-20T02:00:10.207Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["channels","django","javascript","websocket"],"created_at":"2024-10-13T13:30:16.061Z","updated_at":"2025-09-20T02:56:51.622Z","avatar_url":"https://github.com/theY4Kman.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dcrf-client\n\n[![npm version](https://badge.fury.io/js/dcrf-client.svg)](https://badge.fury.io/js/dcrf-client)\n\nThis package aims to provide a **simple**, **reliable**, and **generic** interface to consume [Django Channels REST Framework](https://github.com/hishnash/djangochannelsrestframework) powered WebSocket APIs.\n\nNOTE: This library is a TypeScript port of [channels-api-client](https://github.com/theY4Kman/channels-api-client) to support Django Channels v2, and [@hishnash](https://github.com/hishnash)'s port of [linuxlewis](https://github.com/linuxlewis)'s [channels-api](https://github.com/linuxlewis/channels-api): [djangochannelsrestframework](https://github.com/hishnash/djangochannelsrestframework) and [channelsmultiplexer](https://github.com/hishnash/channelsmultiplexer).\n\n\n## Features\n\n - Promises encapsulating the request/response cycle\n - Subscribe to updates with a callback\n - Automatically reconnect when connection is broken (with backoff — thanks to [reconnecting-websocket](https://github.com/pladaria/reconnecting-websocket))\n - Automatically restart subscriptions on reconnection\n - Requests are queued until a connection is made (no need to wait for connection before sending requests)\n\n\n## Install\n\n```bash\nnpm install dcrf-client\n```\n\n\n## Usage\n\n```javascript\nconst dcrf = require('dcrf-client');\nconst client = dcrf.connect('wss://example.com');\n\nclient.create('people', {name: 'Alex'}).then(person =\u003e {\n  console.info('Created:', person);\n});\n\nclient.retrieve('people', 4).then(person =\u003e {\n  console.info('Retrieved person 4:', person);\n});\n\nclient.update('people', 4, {name: 'Johannes', address: '123 Easy St'}).then(person =\u003e {\n  console.info('Overwrote person 4. Properties after change:', person);\n});\n\nclient.patch('people', 4, {name: 'Jefe'}).then(person =\u003e {\n  console.info('Changed name of person 4. Properties after change:', person);\n});\n\nclient.delete('people', 4).then(() =\u003e {\n  console.info('Deleted person 4. No one liked them, anyway :)');\n});\n\n\n// Subscribe to updates to person 1\nconst personalSubscription = client.subscribe('people', 1, (person, action) =\u003e {\n  if (action === 'update') {\n    console.info('Person 1 was updated:', person);\n  }\n  else if (action === 'delete') {\n    console.info('Person 1 was deleted!');\n  }\n});\n\n// Stop listening for updates\npersonalSubscription.cancel();\n\n\n// Make a generic request to a multiplexer stream\nclient.request('mystream', {key: 'value'}).then(response =\u003e {\n  console.info('Got mystream response, yo:', response);\n});\n\n// Subscribe using a custom action\nconst customSubscription = client.subscribe(\n  'people',\n  {},  // Additional arguments may be passed to action\n  (person, action) =\u003e {\n    if (action === 'create') {\n      console.info(`Person ${person.pk} was created:`, person);\n    }\n    else if (action === 'update') {\n      console.info(`Person ${person.pk} was updated:`, person);\n    }\n    else if (action === 'delete') {\n      console.info(`Person ${person.pk} was deleted!`);\n    }\n  },\n  {\n    includeCreateEvents: true,\n    subscribeAction: 'subscribe_all',\n    unsubscribeAction: 'unsubscribe_all',\n  },\n);\n```\n\n\n## Configuration\n\nThe client can be customized by passing an object as the second argument to `connect()` or `createClient()`. The available options are described below.\n\n```typescript\nconst dcrf = require('dcrf-client');\n\nconst client = dcrf.connect('wss://example.com', {\n  /**\n   * Options to pass along to ReconnectingWebsocket\n   *\n   * See https://github.com/pladaria/reconnecting-websocket#available-options for more info\n   */\n  websocket: {\n    WebSocket?: any; // WebSocket constructor, if none provided, defaults to global WebSocket\n    maxReconnectionDelay?: number; // max delay in ms between reconnections\n    minReconnectionDelay?: number; // min delay in ms between reconnections\n    reconnectionDelayGrowFactor?: number; // how fast the reconnection delay grows\n    minUptime?: number; // min time in ms to consider connection as stable\n    connectionTimeout?: number; // retry connect if not connected after this time, in ms\n    maxRetries?: number; // maximum number of retries\n    maxEnqueuedMessages?: number; // maximum number of messages to buffer until reconnection\n    startClosed?: boolean; // start websocket in CLOSED state, call `.reconnect()` to connect\n    debug?: boolean; // enables debug output\n  },\n\n  /**\n   * Name of serializer field is used to identify objects in subscription event payloads.\n   *\n   * Default: 'pk'\n   */\n  pkField: 'id',\n\n  /**\n   * Whether to ensure subscription delete event payloads store the primary key of the object\n   * in the configured `pkField`, instead of the default 'pk'.\n   *\n   * Because subscription delete payloads aren't run through the configured serializer (as the\n   * objects do not exist), the DCRF backend must pick a field to store the primary key of the\n   * object in the payload. DCRF chooses 'pk' for this field. If `pkField` is *not* 'pk' (and is\n   * instead, say, 'id'), then subscription update payloads will return `{id: 123}`, but delete\n   * payloads will return `{pk: 123}`.\n   *\n   * To address the potential inconsistencies between subscription update and delete payloads,\n   * setting this option to true (default) will cause dcrf-client to replace the 'pk' field with\n   * the configured `pkField` setting.\n   *\n   * Default: true\n   */\n  ensurePkFieldInDeleteEvents: true,\n\n  /**\n   * Customizes the format of a multiplexed message to be sent to the server.\n   *\n   * In almost all circumstances, the default behaviour is usually desired.\n   *\n   * The default behaviour is reproduced here.\n   */\n  buildMultiplexedMessage(stream: string, payload: object): object {\n    return {stream, payload};\n  },\n\n  /**\n   * Customizes the selector (a pattern matching an object) for the response to an API request\n   *\n   * In almost all circumstances, the default behaviour is usually desired.\n   *\n   * The default behaviour is reproduced here.\n   */\n  buildRequestResponseSelector(stream: string, requestId: string): object {\n    return {\n      stream,\n      payload: {request_id: requestId},\n    };\n  },\n\n  /**\n   * Customizes the selector (a pattern matching an object) matching a subscription update event for\n   * an object.\n   *\n   * In almost all circumstances, the default behaviour is usually desired.\n   *\n   * The default behaviour is reproduced here.\n   */\n  buildSubscribeUpdateSelector(stream: string, pk: number, requestId: string): object {\n    return {\n      stream,\n      payload: {\n        action: 'update',\n        data: {[this.pkField]: pk},\n        request_id: requestId,\n      },\n    };\n  },\n\n  /**\n   * Customizes the selector (a pattern matching an object) matching a subscription delete event for\n   * an object.\n   *\n   * In almost all circumstances, the default behaviour is usually desired.\n   *\n   * The default behaviour is reproduced here.\n   */\n  buildSubscribeDeleteSelector(stream: string, pk: number, requestId: string): object {\n    return {\n      stream,\n      payload: {\n        action: 'delete',\n        data: {pk},\n        request_id: requestId,\n      },\n    };\n  },\n\n  /**\n   * Customizes the payload sent to begin subscriptions\n   *\n   * In almost all circumstances, the default behaviour is usually desired.\n   *\n   * The default behaviour is reproduced here.\n   */\n  buildSubscribePayload(pk: number, requestId: string): object {\n    return {\n      action: 'subscribe_instance',\n      request_id: requestId,\n      pk,  // NOTE: the subscribe_instance action REQUIRES the literal argument `pk`.\n           //       this argument is NOT the same as the ID field of the model.\n    };\n  },\n\n  preprocessPayload: (stream, payload, requestId) =\u003e {\n    // Modify payload any way you see fit, before it's sent over the wire\n    // For instance, add a custom authentication token:\n    payload.token = '123';\n    // Be sure not to return anything if you modify payload\n\n    // Or, you can overwrite the payload by returning a new object:\n    return {'this': 'is my new payload'};\n  },\n\n  preprocessMessage: (message) =\u003e {\n    // The \"message\" is the final value which will be serialized and sent over the wire.\n    // It includes the stream and the payload.\n\n    // Modify the message any way you see fit, before its sent over the wire.\n    message.token = 'abc';\n    // Don't return anything if you modify message\n\n    // Or, you can overwrite the the message by returning a new object:\n    return {stream: 'creek', payload: 'craycrayload'};\n  },\n});\n```\n\n\n## Development\n\nThere are two main test suites: unit tests (in `test/test.ts`) to verify intended behaviour of the client, and integration tests (in `test/integration/tests/test.ts`) to verify the client interacts with the server properly.\n\nBoth suites utilize Mocha as the test runner, though the integration tests are executed through py.test, to provide a live server to make requests against.\n\nThe integration tests require separate dependencies. To install them, first [install pipenv](https://pipenv.readthedocs.io/en/latest/install/#installing-pipenv), then run `pipenv install --dev`.\n\nTo run both test suites: `npm run test`\n\nTo run unit tests: `npm run test:unit` or `mocha`\n\nTo run integration tests: `npm run test:integration` or `pipenv run py.test`\n\n\n### How do the integration tests work?\n\n[pytest](https://docs.pytest.org/en/latest/) provides a number of hooks to modify how tests are collected, executed, and reported. These are utilized to discover tests from a Mocha suite, and execute them on pytest's command.\n\nOur pytest-mocha plugin first spawns a subprocess to a custom Mocha runner, which collects its own TypeScript-based tests and emits that test info in JSON format to stdout. pytest-mocha reads this info and reports it to pytest, allowing pytest to print out the true names from the Mocha suite. Using [deasync](https://github.com/abbr/deasync), the Mocha process waits for pytest-mocha to send an acknowledgment (a newline) to stdin before continuing.\n\npytest-mocha then spins up a live Daphne server for the tests to utilize. Before each test, the Mocha suite emits another JSON message informing pytest-mocha which test is about to run. pytest-mocha replies with the connection info in JSON format to the Mocha runner's stdin. The Mocha suite uses this to initialize a DCRFClient for each test.\n\nAt the end of each test, our custom Mocha runner emits a \"test ended\" message. pytest-mocha then wipes the database (with the help of pytest-django) for the next test run.\n\n(Note that technically, Mocha's \"test end\" event is somewhat misleading, and isn't used directly to denote test end. Mocha's \"test end\" demarcates when the test _method_ has completed, but not any `afterEach` hooks. Since we use an `afterEach` hook to unsubscribe all subscriptions from the DCRFClient, care must be taken to ensure the DB remains unwiped and test server remains up until the `afterEach` hook has culminated. To this end, we actually emit our \"test ended\" message right before the next test starts, or the suite ends. See [mochajs/mocha#1860](https://github.com/mochajs/mocha/issues/1860). The logic is inspired by [the workaround](https://github.com/JetBrains/mocha-intellij/commit/03345ee49688e0bca875cba533141c417cefb625) used in JetBrains's mocha-intellij)\n\nNOTE: this is sorta complicated and brittle. it would be nice to refactor this into something more robust. at least for now it provides some assurance the client interacts with the server properly, and also sorta serves as an example for properly setting up a Django Channels REST Framework project.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthey4kman%2Fdcrf-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthey4kman%2Fdcrf-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthey4kman%2Fdcrf-client/lists"}