{"id":18472748,"url":"https://github.com/gregros/wampus","last_synced_at":"2026-03-08T04:31:38.736Z","repository":{"id":143927479,"uuid":"139160535","full_name":"GregRos/wampus","owner":"GregRos","description":"Another WAMP client for JavaScript and TypeScript.","archived":false,"fork":false,"pushed_at":"2020-05-22T12:04:56.000Z","size":1484,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-25T08:06:58.984Z","etag":null,"topics":["events","functional-programming","javascript","library","package","reactive-programming","rpc","rxjs","typescript","wamp","wamp-client","wamp-protocol"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GregRos.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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":"2018-06-29T14:42:16.000Z","updated_at":"2025-05-06T13:17:47.000Z","dependencies_parsed_at":null,"dependency_job_id":"516cc615-0dbc-4290-89f6-232e71804a41","html_url":"https://github.com/GregRos/wampus","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/GregRos/wampus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GregRos%2Fwampus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GregRos%2Fwampus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GregRos%2Fwampus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GregRos%2Fwampus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GregRos","download_url":"https://codeload.github.com/GregRos/wampus/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GregRos%2Fwampus/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262406940,"owners_count":23306284,"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","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":["events","functional-programming","javascript","library","package","reactive-programming","rpc","rxjs","typescript","wamp","wamp-client","wamp-protocol"],"created_at":"2024-11-06T10:22:09.228Z","updated_at":"2026-03-08T04:31:38.690Z","avatar_url":"https://github.com/GregRos.png","language":"TypeScript","readme":"# Wampus.js\n[![Build Status](https://travis-ci.org/GregRos/wampus.svg?branch=master)](https://travis-ci.org/GregRos/wampus)\n[![codecov](https://codecov.io/gh/GregRos/wampus/branch/master/graph/badge.svg)](https://codecov.io/gh/GregRos/wampus)\n[![npm version](https://badge.fury.io/js/wampus.svg)](https://badge.fury.io/js/wampus)\n\nWampus.js is a JavaScript client for the WAMP protocol. The WAMP protocol is a protocol that allows peer-to-peer RPC and PubSub-type communication between different nodes connected to the same server.\n\nFor more information about the WAMP protocol, see **[The official WAMP website](https://wamp-proto.org/)**. \n\nOther examples of WAMP protocol clients:\n\n* [Autobahn|JS](https://github.com/crossbario/autobahn-js), which convinced me to write my own client.\n* [others...](https://wamp-proto.org/implementations/index.html)\n\nWampus works in Node and in the browser.\n\n## Installation\n```bash\nnpm install wampus rxjs typed-wamp transcurse\n```\n\nor:\n\n```bash\nyarn add wampus rxjs typed-wamp transcurse\n```\n\nIf you're running on node, you'll also have to install the `ws` package for web socket support.\n\n```bash\nyarn add ws\n```\n\n1. `rxjs` - this library exposes lots of rxjs observables.\n2. `transcurse` - Used to be part of Wampus in early versions. Implements the transformations provided by the library.\n3. `typed-wamp` - Used to be part of Wampus in early versions. Type definitions and wrappers for WAMP messages, exposed by the library.\n\n## Features\n\n✓ Accurate implementation of the WAMP protocol.\n\n✓ Observable- and Promise- based API, using `rxjs`.\n\n✓ Secondary callback-based API for callers unused to observables.\n\n✓ Primary high-level invocation-based API (e.g. `session.call(\"name\")`).\n\n✓ Secondary low-level message-passing API (e.g. `session.protocol.send(craftedMessage)`) with varying levels of abstraction.\n\n✓  Support for all/most alpha+ advanced profile features.\n\n✓ Human-readable error messages.\n\n✓ Intelligent stack trace collection.\n\n✓  Support for reviving simple JSON into complex objects and vice versa, using [transcurse](https://github.com/GregRos/transcurse).\n\n✓  Runs on both server and client\n\n## Connecting\n\nTo connect to a router use the `connect` method, where you can specify the transport (which includes the type, address, etc)\n\n```typescript\nimport {Wampus} from \"wampus\";\nlet session = Wampus.connect({\n    // transport - required\n    transport : {\n        type : \"websocket\",\n        url : \"ws://localhost:8080\",\n        // serializer - required\n        serializer : \"json\"\n    },\n    // realm - required\n    realm : \"my_realm\"\n})\n```\n\n## Calling\n\nCalling a WAMP procedure is pretty simple. Note that some of the properties are optional.\n\n```typescript\nlet ticket = session.call({\n    name : \"wampus.examples.call\",\n    args : [1, 2, 3],\n    kwargs : {\n        dummy : \"hello\"\n    },\n    options : {\n        // any special protocol options\n    }\n});\n```\n\nIt should be noted that all calls made by Wampus support progress and cancellation by default. If you keep most of the options blank it looks like this:\n\n```typescript\nlet ticket = session.call({name : \"wampus.examples.call\"});\n```\n\nThe `CallTicket` object serves multiple purposes.\n\n### Await final result\n\nThe object is promise-like and can be awaited to receive the final result. Note that you do not get progress reports this way (but an error will cause the promise to reject).\n\n```typescript\nlet finalResult = await ticket;\n```\n\nIt also exposes a regular promise via `ticket.result`:\n\n```typescript\nlet finalResult = ticket.result;\n```\n\n### Progress reports\n\nThe object exposes the observable `.progress`, which yields all data messages (including the final message) and also errors.\n\n```typescript\nticket.progress.subscribe(data =\u003e {\n    if (data.isProgress) {\n        console.log(\"Received progress\", data);\n    } else {\n        console.log(\"Received final\", data);\n    }\n}, err =\u003e {\n    console.error(\"Received error\", err);\n})\n```\n\nYou can also listen to progress reports via an event-like API:\n\n```typescript\nticket.on(\"progress\", data =\u003e {\n    //... same\n})\n```\n\nNote that unlike other methods, event listeners won't propagate errors, since they do not error.\n\n### Cancellation\n\nThe call can be cancelled using the async`.close()` method:\n\n```typescript\nawait ticket.close({\n    mode : \"kill\"\n});\n```\n\nThis will send a cancellation request to the dealer. If the cancellation request is accepted, the call will error with a unique exception (`WampusInvocationCancelledError`).\n\n### Static info\n\nYou can get info about the call using the call ticket.\n\n```typescript\nlet {options, name, callId} = ticket.info;\n```\n\n## Registration\n\nYou register procedures using the `.procedure` method:\n\n```typescript\nlet registrationTicket = await session.procedure({\n    name : \"wampus.examples.register\",\n    options : {\n        // any protocol options\n    },\n    async called(invocationTicket) {\n        return {\n            args : [1, 2]\n        }\n    }\n});\n```\n\nNote that the method returns a promise, and to receive the actual ticket, you must `await` it.\n\nAs part of the method, you supply an `called` function which is called whenever the procedure is invoked.\n\nThe function receives an `InvocationTicket`, which is another type of ticket that contains the invocation info and lets you send progress reports and check if the caller requested cancellation.\n\nThe invocation function, called the `ProcedureHandler`, returns a promise with the final result of the invocation. The function must yield a result of the form:\n\n```\n{\n    args ?: any[];\n    kwargs ?: any;\n    options ?: WampYieldOptions\n}\n```\n\n(In WAMP, every procedure call has to return something, so the best approximation of returning void is returning `{}`).\n\n### Progress reports\n\nYou can report progress by calling `invocationTicket.progress({})`. \n\n```typescript\nlet registrationTicket = await session.procedure({\n    name : \"wampus.examples.register\",\n    async invocation(invocationTicket) {\n        // progress report 1\n        await invocationTicket.progress({\n            kwargs : {\n                percent : 50\n            }\n        });\n        // progress report 2\n        await invocationTicket.progress({\n            kwargs : {\n                percent : 100\n            }\n        });\n        // final result\n        return {\n            kwargs : {\n                result : \"hi!\"\n            }\n        }\n    }\n});\n```\n\n### Cancellation\n\nYou can wait for cancellation using the `invocationTicket.waitForCancel(time)` function. This function lets you wait for a number of milliseconds for a cancellation request to arrive. If it arrives, the function returns a `CancellationTicket`.\n\n```typescript\nlet cancellation = await invocationTicket.waitForCancel(100);\n```\n\nThis tells you that cancellation has been requested, and also allows you to acknowledge the cancellation by calling `cancellation.throw()`, which throws a `WampusInvocationCancelledError`. Provided this error is not caught, it will cause the invocation to end with an error response indicating it has been cancelled.\n\n```typescript\ncancellation.throw(); // Throws a cancellation error\n```\n\nNote that if you do not use this method, you will need to manually throw a `WampusInvocationCancelledError` so that Wampus will know to respond to the invocation correctly.\n\n### Closing the registration\n\nYou close a registration using the `await registrationTicket.close()` method. Closing a registration will still allow you to respond to outstanding invocations as far as Wampus is concerned. However, this matter isn't covered by the WAMP specification so it's implementation defined.\n\n## Publishing\n\nPublishing works similarly to the other options.\n\n```typescript\nawait session.publish({\n    name : \"wampus.examples.publish\",\n    kwargs : {\n        message : \"published!\"\n    },\n    options : {\n        \n    }\n});\n```\n\nUnlike other methods, this method does not return any kind of ticket. \n\nIt returns a promise which resolves immediately, or else once publication acknowledgement is received from the router (if the appropriate option is used).\n\n## Subscribing\n\nSubscribing to a topic uses the `.topic` method:\n\n```typescript\nlet ticket = await session.topic({\n    name : \"wampus.examples.topic\"\n});\n```\n\nEvents are sent via the `ticket.events` observable:\n\n```typescript\nticket.events.subscribe(data =\u003e {\n    console.log(\"received an event\", data);\n});\n```\n\nAnd the `\"event\"` event:\n\n```typescript\nticket.on(\"event\", data =\u003e {\n    \n});\n```\n\n### Unsubscribing\n\nNote that each ticket is a single remote subscription, even though you can subscribe to the ticket's events multiple times. Calling `ticket.close()` will unsubscribe from the topic.\n\n## Debugging\n\nWampus uses a rich system of error objects that are extremely helpful when debugging. \n\nWampus code should always throw errors that extend `WampusError`. Specific error classes depend on the situation, and include:\n\n1. `WampusInvocationError` -  Thrown when a callee responds to an RPC call with an error response.\n2. `WampusIllegalOperationError` - Thrown when a WAMP operation, such as a call, registration, subscription, etc, failed because it was illegal.\n3. `WampusNetworkError` - Thrown when the underlying transport errors or when there is a WAMP protocol failure.\n4. `WampusInvocationCancelledError` - Thrown by code waiting for a cancelled invocation to complete.\n\nError objects can have additional properties that contain more information on the error. Errors that are caused by WAMP messages will have properties such as `args`, `kwargs`, etc. \n\n## Protocol constraints\n\nThis section is necessary in order to put some of Wampus's features in context, and also to show how the constraints of the WAMP protocol affect the way you write your software. It also shows you how Wampus services deal with these constraints.\n\nThis section isn't meant to be a complete guide to the protocol, just to highlight some things and how they influence the library.\n\n### The format of data messages\n\nMost data messages in the WAMP protocol have the following fields:\n\n```\n{\n    args ?: any[];\n    kwargs ?: any;\n    options ?: OperationSpecificOptions;\n}\n```\n\nThis format appears in:\n\n1. Procedure returns (output)\n2. Procedure arguments (input)\n\nSo both returns and arguments can have multiple positional values, as well as one set of keyed values.\n\nProcedures that return successfully MUST reply with a message in this format. That means you can't just return `4`, `undefined`, an array, etc. As a consequence of this, a WAMP procedure cannot return nothing. The closest you can come to that is returning an empty object, `{}`.\n\nIt's not possible to automatically convert from JavaScript's single return value (output) and positional arguments (input) into this format, and Wampus doesn't try to do this. Instead, you'll have to send and receive data in this format yourself.\n\nHowever, Wampus does transform the contents of data messages when they are received and sent. The central idea is to allow you to control how complex objects are flattened, and how flat JSON is revived into a complex object.\n\nThe way it does this is pretty interesting and appears in a later section.\n\n### The format of error messages\n\nWAMP also has a format for error messages sent by invocations. \n\n```\n{\n    args ?: any[];\n    kwargs ?: any[];\n    details ?: any;\n    error : string;\n}\n```\n\nIn this case, `error` contains the name of the error, typically in WAMP naming convention, such as `application.error.no_id_found`. This field can also contain protocol errors such as `wamp.error.no_such_realm`, which are sent by the router and not the callee. \n\n`args` and `kwargs` serve the same purpose as in the data message format.\n\n`details` is another field that can contain an informational object, just like `kwargs`. \n\nWampus lets you revive error responses into full Error objects, and vice versa, using the same type of transformation that's used for the contents of data messages.\n\n## Transform Service\n\nWampus uses [transcurse](https://github.com/GregRos/transcurse), a recursive transformation library, for processing inputs and outputs. Four different transformations are defined:\n\n1. Objects received via args and kwargs, via `in.json`\n2. WAMP error responses, to instances of `Error`, via `in.error`\n3. Objects sent via args and kwargs, via `out.json`\n4. Error objects to WAMP error responses, via `out.error`.\n\nYou can modify the existing transformations by modifying the services.\n\n```typescript\nconst conn = await Wampus.connect({\n    //...\n    services(svcs) {\n        svcs.out.json = svcs.out.json.pre(ctrl =\u003e {\n            //...\n        })\n    }\n})\n```\n\n## Stack trace service\n\nDue to the asynchronous nature of WAMP, there is no stack trace information linking a WAMP message being sent and a WAMP error being received in response to that message.\n\nIn addition, because Wampus heavily uses rxjs, stack traces will tend to be full of rxjs code and little else. This makes such calls hard to debug.\n\nThe stack trace service is an optional service that captures stack traces when an asynchronous call is made and embeds the stack trace into errors thrown as a result of that code. This makes debugging somewhat easier.\n\nThe default stack trace service uses the V8 stack trace API, and so only works in environments that use V8. In other environments, you will need to implement your own stack trace service.\n\nThe stack trace service looks like this:\n\n```\n{\n    capture(ctor : Function) : CallSite[];\n    format(err : Error, callSites : CallSite[]) : string;\n    enabled : boolean;\n}\n```\n\nYou can modify the stack trace service by overwriting it or parts of it in the service initializer:\n\n```typescript\nlet x = Wampus.connect({\n    //...\n    services(svcs) {\n        svcs.stackTraceService = {\n            // my service\n        }\n        \n    }\n})\n```\n\nYou can also just disable it by setting `stackTraceService.enabled = false`.\n\n## Advanced profile support\n\nWampus supports most alpha+ advanced profile features. Here is the breakdown:\n\n| RPC Feature                       | Supported | Support Info                                                 |\n| --------------------------------- | --------- | ------------------------------------------------------------ |\n| progressive_call_results          | ✓         |                                                              |\n| progressive_calls                 | (Sketch)  |                                                              |\n| call_timeout                      | ✓         | Depends on the callee checking cancel requests               |\n| call_canceling                    | ✓         |                                                              |\n| caller_identification             | ✓         | No special implementation needed                             |\n| call_trustlevels                  | ✓         | No special implementation needed                             |\n| registration_meta_api             | ✓         | No special implementation needed                             |\n| pattern_based_registration        | ✓         | No special implementation needed                             |\n| shared_registration               | ✓         | No special implementation needed                             |\n| sharded_registration              | (Sketch)  |                                                              |\n| registration_revocation           | (Sketch)  |                                                              |\n| procedure_reflection              | (Sketch)  |                                                              |\n| **PubSub Feature**                |           |                                                              |\n| subscriber_blackwhite_listing     | ✓         | No special implementation needed                             |\n| publisher_exclusion               | ✓         | No special implementation needed                             |\n| publisher_identification          | ✓         | No special implementation needed                             |\n| publication_trustlevels           | ✓         | No special implementation needed                             |\n| subscription_meta_api             | ✓         | No special implementation needed                             |\n| pattern_based_subscription        | ✓         | No special implementation needed                             |\n| sharded_subscription              | (Sketch)  |                                                              |\n| event_history                     | ✓         | No special implementation needed                             |\n| topic_reflection                  | (Sketch)  |                                                              |\n| **Other Feature**                 |           |                                                              |\n| challenge-response authentication | ✓         | No authentication method is built-in, so challenge response must be done manually by the client. |\n| cookie authentication             | ✗         | Must be manually performed by the user                       |\n| ticket authentication             | ✗         | Must be manually performed by the user                       |\n| rawsocket transport               | ✗         | Currently only WS transport is supported                     |\n| batched WS transport              | (Sketch)  |                                                              |\n| longpoll transport                | ✗         | Currently only WS transport is supported                     |\n| session meta api                  | ✓         | No special implementation needed                             |\n| MessagePack serialization         | ✗         | Only JSON serialization is implemented                       |\n\n## Comments\n\nOriginally, this library was supposed to have an API completely based around cold observables. For example, using `call` would return an observable that performs the call when subscribed to. Unsubscribing would cancel the call.\n\nHowever, I had to abandon this design due to technical limitations.\t","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgregros%2Fwampus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgregros%2Fwampus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgregros%2Fwampus/lists"}