{"id":18554304,"url":"https://github.com/oracle/coherence-js-client","last_synced_at":"2025-04-09T23:31:09.499Z","repository":{"id":38208445,"uuid":"269443816","full_name":"oracle/coherence-js-client","owner":"oracle","description":"The Coherence JavaScript Client","archived":false,"fork":false,"pushed_at":"2025-03-24T04:38:59.000Z","size":2138,"stargazers_count":10,"open_issues_count":1,"forks_count":8,"subscribers_count":13,"default_branch":"main","last_synced_at":"2025-03-24T05:28:51.723Z","etag":null,"topics":["caching","clustering","coherence","data-grid","distributed","grpc","grpc-client","grpc-node","in-memory","kv-store","microservices","polyglot","scalability"],"latest_commit_sha":null,"homepage":"https://oracle.github.io/coherence-js-client/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"upl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oracle.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-06-04T19:12:14.000Z","updated_at":"2025-03-24T04:39:00.000Z","dependencies_parsed_at":"2024-01-20T01:59:31.632Z","dependency_job_id":"9864151d-c990-4e2b-b5eb-ba7ec4401df0","html_url":"https://github.com/oracle/coherence-js-client","commit_stats":{"total_commits":336,"total_committers":9,"mean_commits":"37.333333333333336","dds":"0.29761904761904767","last_synced_commit":"4b11d9d8caf14c559dabbd07d381781c9f8489d6"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oracle%2Fcoherence-js-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oracle%2Fcoherence-js-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oracle%2Fcoherence-js-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oracle%2Fcoherence-js-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oracle","download_url":"https://codeload.github.com/oracle/coherence-js-client/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248129618,"owners_count":21052606,"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":["caching","clustering","coherence","data-grid","distributed","grpc","grpc-client","grpc-node","in-memory","kv-store","microservices","polyglot","scalability"],"created_at":"2024-11-06T21:21:10.015Z","updated_at":"2025-04-09T23:31:09.484Z","avatar_url":"https://github.com/oracle.png","language":"TypeScript","readme":"# JavaScript Client for Oracle Coherence\n\nThe JavaScript Client for Oracle Coherence allows Node applications to act as\ncache clients to a Coherence Cluster using Google's `gRPC` framework for\nthe network transport.\n\n### Features\n* Familiar `Map`-like interface for manipulating entries\n* Cluster-side querying and aggregation of map entries\n* Cluster-side manipulation of map entries using `EntryProcessors`\n* Registration of listeners to be notified of map mutations\n\n### Requirements\n* Coherence CE versions `22.06`, `14.1.2-0-0`, `25.03 or later (or equivalent non-open source editions) with a configured [gRPC Proxy](https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.2206/develop-remote-clients/using-coherence-grpc-server.html)\n* Node `18.15.x` or later\n* NPM `9.x` or later\n\n### Usage\n\nBefore testing the library, you must ensure a Coherence cluster is available.  For local development, we recommend using the Coherence CE Docker image; it contains everything necessary for the client to operate correctly.\n\n```bash\ndocker run -d -p 1408:1408 ghcr.io/oracle/coherence-ce:25.03\n```\n\nor to save some keystrokes/time, use the included npm script, `coh-up` to start a two-member Cluster with the gRPC port at 1408\"\n```bash\nnpm run coh-up\n```\n\n**Important!** When calling `coh-up` or `coh-down`, the LTS version of Coherence will be used (`22.06.11`).\nTo use a later Coherence version, such as `25.03`, prefix the calls with, or export `COHERENCE_VERSION=\u003cdesired-version\u003e`.\nFor example:\n```bash\nCOHERENCE_VERSION=25.03 npm run coh-up\n```\n\nFor more details on the image, see the [documentation](https://github.com/oracle/coherence/tree/master/prj/coherence-docker).\n\n### Declare Your Dependency\n\nTo use the JavaScript Client for Oracle Coherence, simply declare it as a dependency in your\nproject's `package.json`:\n```\n...\n\"dependencies\": {\n    \"@oracle/coherence\": \"^1.2\",\n},\n...\n```\n\n### Compatibility with Java Types\nThe following table provides a listing of mappings between Java types and Javascript types when working with\nCoherence `25.03` or later.  If using Coherence `22.06.x`, these types will be returned as Number.  It is recommended\nusing `25.03` if intentionally using `java.math.BigInteger` or `java.math.BigDecimal` as part of your application.\n\n| Java Type            | JavascriptType         |\n|----------------------|------------------------|\n| java.math.BigInteger | BigInt (ECMA standard) |\n| java.math.BigDecimal | Decimal ([decimal.js](https://www.npmjs.com/package/decimal.js))   |\n\n### Examples\n\n\u003e NOTE: The following examples assume the Coherence container is running locally.\n\u003e You can start a container by running `npm run coh-up`.\n\n#### Establishing a Session\n\nThe Coherence uses the concept of a `Session` to manage a set of related Coherence resources,\nsuch as maps and/or caches. When using the JavaScript Client for Oracle Coherence, a `Session` connects to a specific\ngRPC endpoint and uses a specific serialization format to marshal requests and responses.\nThis means that different sessions using different serializers may connect to the same server endpoint. Typically,\nfor efficiency the client and server would be configured to use matching serialization formats to avoid\ndeserialization of data on the server, but this does not have to be the case. If the server is using a different\nserializer for the server-side caches, it must be able to deserialize the client's requests, so there must be\na serializer configured on the server to match that used by the client.\n\n\u003e NOTE: Currently, the JavaScript Client for Oracle Coherence only supports JSON serialization\n\nA `Session` is constructed using an `Options` instance, or a generic object with the same keys and values.\n\nThe currently supported properties are:\n* `address` - the address of the Coherence gRPC proxy.  This defaults to `localhost:1408`.\n* `requestTimeoutInMillis` - the gRPC request timeout in milliseconds.  This defaults to `60000`.\n* `callOptions` - per-request gRPC call options.\n* `tls` - options related to the configuration of TLS.\n    - `enabled` - determines if TLS is enabled or not.  This defaults to `false` (NOTE: assumes `true` if all three `COHERENCE_TLS_*` (see subsequent bullets) environment variables are defined)\n    - `caCertPath` - the path to the CA certificate.  This may be configured using the environment variable `COHERENCE_TLS_CERTS_PATH`\n    - `clientCertPath` - the path to the client certificate. This may be configured with the environment variable `COHERENCE_TLS_CLIENT_CERT`\n    - `clientKeyPath` - the path to the client certificate key. This may be configured with the environment variable `COHERENCE_TLS_CLIENT_KEY`\n\n*NOTE*: If testing locally generated certificates, set `COHERENCE_IGNORE_INVALID_CERTS` to `true` to disable\nTLS validation of the certificates.\n\n```typescript\nconst { Session } = require('@oracle/coherence')\n\nlet session = new Session()\n```\n\nThis is the simplest invocation which assumes the following defaults:\n* `address` is `localhost:1408`\n* `requestTimeoutInMillis` is `60000`\n* `tls` is `disabled`\n\nTo use values other than the default, create a new `Options` instance, configure as desired,\nand pass it to the constructor of the `Session`:\n\n```javascript\nconst { Session, Options } = require('@oracle/coherence')\n\nconst opts = new Options()\nopts.address = 'example.com:4444'\n\nlet session = new Session(opts)\n```\n\nor instead of an `Options` instance, using a generic JavaScript object:\n\n```javascript\nconst { Session } = require('@oracle/coherence')\n\nconst opts = new Options({address: 'example.com:4444'})\n\nlet session = new Session(opts)\n```\n\nAs of v1.2.3 of the JavaScript Client for Oracle Coherence, it's now possible to use the Coherence\nNameService to lookup gRPC Proxy endpoints.  The format to enable this feature is\n`coherence:///\u003chost\u003e([:port]|[/cluster-name]|[:port/cluster-name])`\n\nFor example:\n * `coherence:///localhost` will connect to the name service bound to a local coherence cluster on port `7574` (the default Coherence cluster port).\n * `coherence:///localhost:8000` will connect to the name service bound to a local coherence cluster on port `8000`.\n * `coherence:///localhost/remote-cluster` will connect to the name service bound to a local coherence cluster on port `7574` (the default Coherence cluster port) and look up the name service for the given cluster name.  Note: this typically means both clusters have a local member sharing a cluster port.\n * `coherence:///localhost:8000/remote-cluster` will connect to the name service bound to a local coherence cluster on port `8000` and look up the name service for the given cluster name.  Note: this typically means both clusters have a local member sharing a cluster port.\n\nWhile this is useful for local development, this may have limited uses in a production environment.  For example,\nCoherence running within a container with the cluster port (`7574`) exposed so external clients may connect.  The\nlookup will fail to work for the client as the Coherence name service return a private network address which\nwon't resolve. Lastly, if connecting to a cluster that has multiple proxies bound to different ports, gRPC, by default,\nwill use the first address returned by the resolver.  It is possible to enable round-robin load balancing by including\na custom channel option when creating the session:\n\n```typescript\nconst { Session } = require('@oracle/coherence')\n\nconst opts = new Options({address: 'coherence:///localhost',\n  channelOptions: {'grpc.service_config': JSON.stringify({ loadBalancingConfig: [{ round_robin: {} }], })}})\n\nlet session = new Session(opts)\n```\n\n*NOTE* The Coherence NameService feature requires Node `20.x` or later.\n\nIt's also possible to control the default address the session will bind to by providing\nan address via the `COHERENCE_SERVER_ADDRESS` environment variable.  The format of the value would\nbe the same as if you configured it programmatically as the above example shows.\n\nOnce the session has been constructed, it will now be possible to create maps and caches.\n\n#### Basic Map Operations\n\nThe map (`NamedMap`) and cache (`NamedCache`) implementations provide the same basic features as the Map provided\nby JavaScript except for the following differences:\n\n* key equality isn't restricted to reference equality\n* insertion order is not maintained\n* `set()` calls cannot be chained because of the asynchronous nature of the API\n\n\u003e NOTE: The only difference between `NamedCache` and `NamedMap` is that the 'NamedCache' allows associating a\n\u003e `time-to-live` on the cache entry, while `NamedMap` does not\n\nFor the following examples, let's assume that we have a Map defined in Coherence named `Test`.\nTo get access to the map from the client:\n\n\u003e NOTE: If using the Docker image previously mentioned for testing, you don't need to worry about the details of the map name.  Any name will work.\n\n```javascript\nlet map = session.getMap('Test')\n```\n\nOnce we have a handle to our map, we can invoke the same basic operations as a standard JavaScript Map:\n```javascript\nawait map.size\n// (zero)\n\nawait map.set('key1', 'value1')\nawait map.set('key2', 'value2')\n// returns a Promise vs the map itself, so these can't be chained\n\nawait map.size\n// (two)\n\nawait map.get('key1')\n// value1\n\nawait map.has('key2')\n// true\n\nawait map.has('key3')\n// false\n\nawait map.keys()\n// ['key1', 'key2']\n\nawait map.values()\n// ['value1', 'value2']\n\nawait map.entries()\n// [{key: 'key1', value: 'value1'}, {key: 'key2', value: 'value2'}]\n\nawait map.forEach((value, key) =\u003e console.log(key + ': ' + value))\n// prints all of the entries\n```\n\n#### Querying the Map\n\nCoherence provides a rich set of primitives that allow developers to create advanced queries against\na set of entries returning only those keys and/or values matching the specified criteria.\nSee the [documentation](https://oracle.github.io/coherence/23.09/api/java/index.html) for details\non the Filters provided by this client.\n\nLet's assume we have a `NamedMap` in which we're storing `string` keys and some objects with the structure of:\n\n```\n{\n  name: \u003cstring\u003e\n  age:  \u003cnumber\u003e\n  hobbies: [] // of string\n}\n```\n\nFirst, let's insert a few objects:\n\n```javascript\nawait map.set('0001', {name: \"Bill Smith\", age: 38, hobbies: [\"gardening\", \"painting\"]})\nawait map.set('0002', {name: \"Fred Jones\", age: 56, hobbies: [\"racing\", \"golf\"]})\nawait map.set('0003', {name: \"Jane Doe\", age: 48, hobbies: [\"gardening\", \"photography\"]})\n```\n\nUsing a filter, we can limit the result set returned by the map:\n\n```javascript\nconst { Filters } = require('@oracle/coherence')\n\n// ...\n\nawait map.entries(Filters.greater('age', 40))\n// [{key: '0002', value: {name: \"Fred Jones\"...}}, {key: '0002', value: {name: \"Jane Doe\"...}}]\n\nawait map.keys(Filters.arrayContains('hobbies', 'gardening'))  \n// ['0001', '0003']\n\nawait map.values(Filters.not(Filters.arrayContains('hobbies', 'gardening')))\n// [{name: \"Fred Jones\", age: 56, hobbies: [\"racing\", \"golf\"]}]\n```\n\n#### Aggregation\n\nCoherence provides developers with the ability to process some subset of the entries in a map,\nresulting in an aggregated result. See the [documentation](https://oracle.github.io/coherence/23.09/api/java/index.html) for aggregators provided by this client.\n\nAssume the same set of keys and values are present from the filtering example above:\n\n```javascript\nconst { Aggregators, Filters } = require('@oracle/coherence')\n\n// ...\n\nawait map.aggregate(Aggregators.average('age'))\n// 47.3\n\nawait map.aggregate(Aggregators.sum('age'))\n// 142\n\nawait map.aggregate(Filters.greater('age', 40), Aggregators.count())\n// 2\n```\n\n#### Entry Processing\n\nAn entry processor allows mutation of map entries in-place within the cluster instead of bringing the entire object\nto the client, updating, and pushing the value back.  See the [documentation](https://oracle.github.io/coherence/23.09/api/java/index.html) for the processors provided by this client.\n\nAssume the same set of keys and values are present from the filtering and aggregation examples:\n\n```javascript\nconst { Filters, Processors } = require('@oracle/coherence')\n\n// ...\n\n// targeting a specific entry\nawait map.invoke('0001', Processors.extract('age'))\n// returns: 38\n\n// target all entries across the cluster\nawait map.invokeAll(Processors.extract('age'))\n// returns: [['0001', 38], ['0002', 56], ['0003', 48]]\n\n// target all entries matching filtered critera\nawait map.invokeAll(Filters.greater('age', 40), Processors.extract('age'))\n// returns: [['0002', 56], ['0003', 48]]\n\n// incrementing a number 'in-place'\nawait map.invokeAll(Filters.greater('age', 40), Processors.increment('age', 1))\n// returns [['0002', 57], ['0003', 49]]\n\n// update a value 'in-place'\nawait map.invoke('0001', Processors.update('age', 100))\n// returns true meaning the value was updated\nawait map.get('0001')\n// the value will reflect the new age value\n```\n\n### Events\n\nCoherence provides the ability to subscribe to notifications pertaining to a particular map/cache.\nRegistration works similarly to event registration with Node, with some key differences.  In addition\nto listening for specific events, it is possible to listen to events for changes made to a specific key, or using\na Filter, it's possible to limit the events raised to be for a subset of the map entries.\n\nNow, let's register a listener:\n\n```javascript\nimport { event } from '@oracle/coherence'\n\nconst MapEventType = event.MapEventType\nconst MapListener = event.MapListener\n\nconst handler = (event: MapEvent) =\u003e { \n  console.log('Event: ' + event.description \n    + ', Key: ' + JSON.stringify(event.key) \n    + ', New Value: ' + JSON.stringify(event.newValue)\n    + ', Old Value: ' + JSON.stringify(event.oldValue))\n}\n\nconst listener = new MapListener()\n  .on(MapEventType.INSERT, handler)\n  .on(MapEventType.UPDATE, handler)\n  .on(MapEventType.DELETE, handler)\n\n// register to receive all event types for all entries within the map\nawait map.addMapListener(listener)\n\nawait map.set('a', 'b')\n// Event: insert, Key: a, New Value: b, Old Value: null\n\nawait map.set('a', 'c')\n// Event: update, Key: a, New Value: c, Old Value: b\n\nawait map.delete('a')\n// Event: delete, Key: a, New Value: null, Old Value: c\n\n// remove the listeners\nawait map.removeMapListener(listener)\n\n// =======================================\n\n// Assume the previous listener as well as the following key and values\n//   ['0001', {name: \"Bill Smith\", age: 38, hobbies: [\"gardening\", \"painting\"]}]\n//   ['0002', {name: \"Fred Jones\", age: 56, hobbies: [\"racing\", \"golf\"]}]\n//   ['0003', {name: \"Jane Doe\", age: 48, hobbies: [\"gardening\", \"photography\"]}]\n\n// Add handlers for updates to '0001'\nawait map.addMapListener(listener, '0001')\n\nawait map.update('0002', '0002')\n// does not generate any events\n\nawait map.invoke('0001', Processors.increment('age', 1))\n// Event: update, Key: 0001, New Value: {name: \"Bill Smith\", age: 39, hobbies: [\"gardening\", \"painting\"]}, Old Value: {name: \"Bill Smith\", age: 38, hobbies: [\"gardening\", \"painting\"]}\n\nawait map.delete('0001') \n// does not generate any events\n\n// remove the key listener\nawait map.removeMapListener(listener, '0001')\n\n// =======================================\n\n// Assume the same setup as the previous example, except instead of listening to events for a single key,\n// we'll instead listen for events raised for entries that match the filtered criteria.\nconst filter = Filters.event(Filters.greater('age', 40), filter.MapEventFilter.UPDATED)\n\n// Listen to all updates to entries where the age property of the entry value is greater than 40\nawait map.addMapListener(listener, filter) \n\nawait map.invokeAll(Processors.increment('age', 1));\n// Event: update, Key: 0002, New Value: {name: \"Fred Jones\", age: 57, hobbies: [\"racing\", \"golf\"]}, Old Value: {name: \"Fred Jones\", age: 56, hobbies: [\"racing\", \"golf\"]}\n// Event: update, Key: 0003, New Value: \"Jane Doe\", age: 49, hobbies: [\"gardening\", \"photography\"]}, Old Value: \"Jane Doe\", age: 48, hobbies: [\"gardening\", \"photography\"]}\n\n// remove the filter listener\nawait map.removeMapListener(listener, filter)\n```\n\n### Cut/Paste Example\nHere's an example that can be pasted into a new node project that is using this library:\n\n```javascript\nconst { Session } = require('@oracle/coherence')\n\nlet session = new Session()\nlet map = session.getMap('Test')\n\nsetImmediate(async () =\u003e {\n  console.log(\"Map size is \" + (await map.size))\n\n  console.log(\"Inserting entry (key=1, value=One)\")\n  await map.set(1, \"One\")\n\n  console.log(\"Inserting entry (key=2, value=Two)\")\n  await map.set(2, \"Two\")\n\n  let entries = await map.entries();\n\n  console.log(\"All entries\")\n  for await (const entry of entries) {\n    console.log(entry.key + '=' + entry.value)\n  }\n\n  console.log(\"Key 1 is \" + (await map.get(1)))\n  console.log(\"Key 2 is \" + (await map.get(2)))\n\n  console.log(\"Deleting entry (key=1)\")\n  await map.delete(1)\n\n  console.log(\"Map size is \" + (await map.size))\n  await session.close()\n})\n```\n\nWhen run, it produces:\n\n```bash\nMap size is 0\nInserting entry (key=1, value=One)\nMap entry is One\nDeleting entry (key=1)\nMap size is 0\n```\n\n### References\n* Oracle Coherence JavaScript Client - https://oracle.github.io/coherence-js-client/\n* Oracle Coherence CE Documentation - https://coherence.community/23.09/docs/#/docs/about/01_overview\n\n## Contributing\n\nThis project welcomes contributions from the community. Before submitting a pull request, please [review our contribution guide](./CONTRIBUTING.md)\n\n## Security\n\nPlease consult the [security guide](./SECURITY.md) for our responsible security vulnerability disclosure process\n\n## License\n\nCopyright (c) 2020, 2023 Oracle and/or its affiliates.\n\nReleased under the Universal Permissive License v1.0 as shown at\n\u003chttps://oss.oracle.com/licenses/upl/\u003e.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foracle%2Fcoherence-js-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foracle%2Fcoherence-js-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foracle%2Fcoherence-js-client/lists"}