Skip to content

Instantly share code, notes, and snippets.

@SReject
Last active July 25, 2024 17:45
Show Gist options
  • Select an option

  • Save SReject/57c194719632d0e14c6dae7461b29567 to your computer and use it in GitHub Desktop.

Select an option

Save SReject/57c194719632d0e14c6dae7461b29567 to your computer and use it in GitHub Desktop.

Revisions

  1. SReject revised this gist Jul 25, 2024. 1 changed file with 52 additions and 17 deletions.
    69 changes: 52 additions & 17 deletions ire-client.ts
    Original file line number Diff line number Diff line change
    @@ -24,37 +24,61 @@ export default class IREClient {
    handler?: (data: unknown) => void;
    }>>{};
    #transport: IRETransport;
    #methods = <Record<string, (...args: unknown[]) => unknown>>{};

    #closeHandler = () => {
    this.close();
    }
    #messageHandler = ({ data }) => {
    #messageHandler = async ({ data }) => {
    if (data == null || typeof data !== 'string') {
    return
    }
    try {
    const message = <IREMessage>JSON.parse(data);

    const { type, id, name, data: body } = message;

    if (
    message.type !== 'event' ||
    message.id !== 0 ||
    typeof message.name !== 'string' ||
    Object.hasOwn(this.#handlers, message.name) ||
    this.#handlers[message.name].length === 0
    type === 'invoke' &&
    typeof name === 'string' &&
    Object.hasOwn(this.#methods, name)
    ) {
    return
    }
    const method = this.#methods[name];
    try {
    const result = await method(...<Array<unknown>>(body || []));
    this.#transport.send({
    type: 'reply',
    name: 'success',
    id,
    data: result
    })
    } catch (err) {
    this.#transport.send({
    type: 'reply',
    name: 'error',
    id,
    data: err.message
    });
    }

    const handlerList = this.#handlers[message.name];
    let idx = 0;
    while (idx < handlerList.length) {
    const { handler, once } = handlerList[idx];
    if (once) {
    handlerList.splice(idx, 1);
    } else {
    idx += 1;
    } else if (
    type === 'event' &&
    id === 0 &&
    typeof name === 'string' &&
    Object.hasOwn(this.#handlers, message.name) &&
    this.#handlers[message.name].length === 0
    ) {
    const handlerList = this.#handlers[message.name];
    let idx = 0;
    while (idx < handlerList.length) {
    const { handler, once } = handlerList[idx];
    if (once) {
    handlerList.splice(idx, 1);
    } else {
    idx += 1;
    }
    handler(message.data);
    }
    handler(message.data);
    }
    } catch {}
    };
    @@ -132,6 +156,17 @@ export default class IREClient {
    this.#transport.send(JSON.stringify({ type: "event", name, id: 0, data }))
    }

    /** Registers a method the remote can invoke */
    registerMethod(name: string, handler: (...args: unknown[]) => unknown) {
    if (this.#methods == null) {
    this.#methods = {};
    }
    if (Object.hasOwn(this.#methods, name)) {
    throw new Error('METHOD_ALREADY_REGISTERED');
    }
    this.#methods[name] = handler;
    }

    close() {
    const offEvent = this.#transport.removeEventListener ? 'removeEventListener' : 'off';
    Object.values(this.#pending).forEach(pending => {
  2. SReject revised this gist Jul 25, 2024. 1 changed file with 148 additions and 0 deletions.
    148 changes: 148 additions & 0 deletions ire-client.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,148 @@
    type EventHandler = (event: unknown) => void;

    interface IRETransport {
    send: (data: unknown) => void;
    on?: (event: string, handler: EventHandler) => void;
    addEventListener?: (event: string, handler: EventHandler) => void;
    off?: (event: string, handler: EventHandler) => void;
    removeEventListener?: (event: string, handler: EventHandler) => void;
    }

    interface IREMessage {
    type: "invoke" | "reply" | "event";
    name: string;
    id: number|string;
    data: unknown
    }

    export default class IREClient {
    #handlers = <Record<string, {handler: EventHandler, once: boolean}[]>>{};
    #lastId = 0;
    #pending = <Record<number, {
    promise: Promise<unknown>,
    reject?: (reason: unknown) => void
    handler?: (data: unknown) => void;
    }>>{};
    #transport: IRETransport;

    #closeHandler = () => {
    this.close();
    }
    #messageHandler = ({ data }) => {
    if (data == null || typeof data !== 'string') {
    return
    }
    try {
    const message = <IREMessage>JSON.parse(data);

    if (
    message.type !== 'event' ||
    message.id !== 0 ||
    typeof message.name !== 'string' ||
    Object.hasOwn(this.#handlers, message.name) ||
    this.#handlers[message.name].length === 0
    ) {
    return
    }

    const handlerList = this.#handlers[message.name];
    let idx = 0;
    while (idx < handlerList.length) {
    const { handler, once } = handlerList[idx];
    if (once) {
    handlerList.splice(idx, 1);
    } else {
    idx += 1;
    }
    handler(message.data);
    }
    } catch {}
    };
    constructor(transport: IRETransport) {
    const addEvent = transport.addEventListener ? 'addEventListener' : 'on';
    this.#transport = transport;
    this.#transport[addEvent]('close', this.#closeHandler);
    this.#transport[addEvent]('error', this.#closeHandler);
    this.#transport[addEvent]('message', this.#messageHandler)
    }

    /** Adds an event handler */
    on(event: string, handler: EventHandler, once: boolean = false) {
    if (this.#handlers == null) {
    this.#handlers = {};
    }
    if (!Object.hasOwn(this.#handlers, event)) {
    this.#handlers[event] = [];
    }
    this.#handlers[event].push({ handler, once });
    }

    /** Adds a one-time event handler */
    once(event: string, handler: EventHandler) {
    this.on(event, handler, true);
    }

    /** Removes a previously added event handler */
    off(event: string, handler: EventHandler, once: boolean = false) {
    if (
    this.#handlers == null ||
    !Object.hasOwn(this.#handlers, event) ||
    this.#handlers[event].length === 0
    ) {
    return;
    }
    const idx = this.#handlers[event].findLastIndex(({ handler: regHandler, once: regOnce}) => handler === regHandler && once === regOnce);
    if (idx > -1) {
    this.#handlers[event].splice(idx, 1);
    }
    }

    /** Invokes a method on the remote and returns the result */
    invoke<T = unknown>(name: string, ...data: unknown[]) : Promise<T> {
    const id = ++this.#lastId;
    this.#pending[id] = {
    promise: new Promise<T>((resolve, reject) => {
    this.#pending[id].reject = reject;
    const handler = this.#pending[id].handler = (data: string) => {
    try {
    const message = <IREMessage>JSON.parse(data);
    if (message == null || message.type !== "reply" || message.id !== id) {
    return;
    }
    this.#pending[id] = null;

    this.#transport[this.#transport.removeEventListener ? 'removeEventListener' : 'off']('message', handler);

    if (message.name === 'success') {
    resolve(<T>message.data);
    } else {
    reject(new Error(<string>message.data || 'UNKNOWN_ERROR'));
    }
    } catch { }
    };
    this.#transport[this.#transport.addEventListener ? 'addEventListener' : 'on']('message', handler);
    this.#transport.send(JSON.stringify({ type: "invoke", name, id, data }))
    })
    };
    return <Promise<T>>this.#pending[id].promise;
    }

    /** Emits the given event on the remote */
    emit(name: string, data: unknown) {
    this.#transport.send(JSON.stringify({ type: "event", name, id: 0, data }))
    }

    close() {
    const offEvent = this.#transport.removeEventListener ? 'removeEventListener' : 'off';
    Object.values(this.#pending).forEach(pending => {
    if (pending != null) {
    pending.reject(new Error('TRANSPORT_CLOSED'));
    this.#transport[offEvent]('message', pending.handler);
    }
    });
    this.#pending = {};
    this.#transport[offEvent]('close', this.#closeHandler);
    this.#transport[offEvent]('error', this.#closeHandler);
    this.#transport[offEvent]('message', this.#messageHandler);
    }
    }
  3. SReject revised this gist Jun 23, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion IRE Example.md
    Original file line number Diff line number Diff line change
    @@ -47,7 +47,7 @@ Server broadcasts a message to all clients:
    ```js
    {
    "type": "event",
    "id": null,
    "id": "authMe",
    "name": "userConnect",
    "data": {"type": "guest"}
    }
  4. SReject renamed this gist Jun 23, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  5. SReject revised this gist Jun 23, 2023. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,14 +1,14 @@
    # Invoke, Response, Event
    The goal of the Invoke, Response, Event protocol is to provide a generic meaningful format for communications while providing mechanisms related to querying resources and transmiting state change notifications.
    The goal of the Invoke, Response, Event protocol is to provide a generic meaningful format for communications while providing mechanisms related to querying resources and transmitting state change notifications.

    <br>

    # Message Formats
    Messages are transmitted as valid JSON-parsable strings and must conform to one of the message-formats given below.
    Messages are transmitted as JSON-parsable strings and must conform to one of the message-formats given below.

    All format-defined properties are required to be part of the resulting JSON string. If a defined property does not have an applicable value, it's value should default to `null`.

    Implementations must **NOT** define or rely upon any property not defined in this format. Any message property not defined in by this document must be ignored by the recipient.
    Implementations must **NOT** define or rely upon any property not defined in this format. Any message property not defined in this document must be ignored by the recipient.

    Recipients of messages containing properties not defined by this document may treat the inbound message as invalid.

  6. SReject revised this gist Jun 23, 2023. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -8,9 +8,9 @@ Messages are transmitted as valid JSON-parsable strings and must conform to one

    All format-defined properties are required to be part of the resulting JSON string. If a defined property does not have an applicable value, it's value should default to `null`.

    Implementations should **NOT** define or rely upon any property not defined in this format. Any non format-defined properties in the message's JSON string are to be ignored by the recipient.
    Implementations must **NOT** define or rely upon any property not defined in this format. Any message property not defined in by this document must be ignored by the recipient.

    Messages that are non-conformant may be treated as invalid, discarded, or allowed to passed through.
    Recipients of messages containing properties not defined by this document may treat the inbound message as invalid.

    <br>

  7. SReject revised this gist Jun 23, 2023. 1 changed file with 12 additions and 17 deletions.
    29 changes: 12 additions & 17 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,9 @@
    # Invoke, Response, Event
    The goal of the Invoke, Response, Event protocol is to provide a generic meaningful format for communications while providing mechanisms related to querying resources and transmiting state change notifications.

    ## Message Formats
    <br>

    # Message Formats
    Messages are transmitted as valid JSON-parsable strings and must conform to one of the message-formats given below.

    All format-defined properties are required to be part of the resulting JSON string. If a defined property does not have an applicable value, it's value should default to `null`.
    @@ -18,15 +20,12 @@ Invoke messages are used to query or update a remote resource. These messages mu
    Invoke messages may be processed in any order regardless of the order in which they were received; this is to allow for parallel and asynchronous processing of messages.


    | Property | Type | Nullable | Description |
    |------------|:--------------------:|:--------:|-------------------------------------------------------------|
    | `type` | `"invoke" ` | no | Literal text `invoke` |
    | `id`\* | `number` \| `string` | no | A sender-generated ID |
    | `name` | `string` | no | The resource identifier to query |
    | `data`\** | `Array<any>` | no | A list of values(arguments) to pass to the resource handler |

    \*: The `id` must be unique to other pending innvocations but may be reused once a response has been received.
    \*\*: If no values are to be provided to the resource `data` should be an empty array.
    | Property | Type | Nullable | Description |
    |----------|:--------------------:|:--------:|-------------------------------------------------------------|
    | `type` | `"invoke" ` | no | Literal text `invoke` |
    | `id` | `number`<br>`string` | no | A unique sender-generated id used to associate messages with the invocation<br>The `id` must be unique to other pending innvocations but may be reused once a response has been received. |
    | `name` | `string` | no | The resource identifier to query |
    | `data` | `Array<any>` | no | A list of values(arguments) to pass to the resource handler<br>If no values are to be provided to the resource `data` should be an empty array. |

    <br>

    @@ -35,13 +34,11 @@ Upon receiving a [`Invoke`](#invoke) message, the recipiant should process the i

    Invoke messages that result in a state change that would generate an event *should* generate the [`Event`](#event) as well as the [`Response`](#response)

    <br>

    #### Success
    | Property | Type | Nullable | Description |
    |----------|:--------------------:|:--------:|----------------------------------------------------|
    | `type` | `"response"` | no | Literal text `response` |
    | `id` | `number` \| `string` | no | The `id` property received with the invoke-message |
    | `id` | `number`<br>`string` | no | The `id` property received with the invoke-message |
    | `name` | `"success"` | no | Literal text `success` |
    | `data` | *any* | yes | Resulting value from processing the query |

    @@ -51,7 +48,7 @@ Invoke messages that result in a state change that would generate an event *shou
    | Property | Type | Nullable | Description |
    |----------|:--------------------:|:--------:|-------------------------------------------------------|
    | `type` | `"response"` | no | Literal text `response` |
    | `id` | `number` \| `string` | no | The `id` property received with the invoke-message |
    | `id` | `number`<br>`string` | no | The `id` property received with the invoke-message |
    | `name` | `"error"` | no | Literal text `error` |
    | `data` | *any* | yes | Error information detailing why the invocation failed |

    @@ -63,12 +60,10 @@ An event-message is used to notify the recipient of a state change. Event-messag
    | Property | Type | Nullable | Description |
    |----------|:--------------------:|:--------:|------------------------------------|
    | `type` | `"event"` | no | Literal text `event` |
    | `id`\* | `number` \| `string` | yes | Ignored |
    | `id` | `number`<br>`string` | yes | If the event is the result of a received invocation the `id` should be the `id` of the invocation otherwise it should be `null`. This is so clients can filter out events that are the direct result of an invocation |
    | `name` | `string` | no | Event name/Notification identifier |
    | `data` | *any* | yes | The data accompanying the event |

    \*: If the event is the result of a received invocation the `id` should be the `id` of the invocation otherwise it should be `null`. This is so clients can filter out events that are the direct result of an invocation


    # Notes
    This is heavily inspired by [jsonrpc](https://www.jsonrpc.org/specification) with a focus on the format and ignoring implementation.
  8. SReject revised this gist Jun 23, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -67,7 +67,7 @@ An event-message is used to notify the recipient of a state change. Event-messag
    | `name` | `string` | no | Event name/Notification identifier |
    | `data` | *any* | yes | The data accompanying the event |

    \*: If the event is the result of a received invocation the `id` should be the `id` of the invocation only for the client that issued the invocation, otherwise it should be `null`. This is so clients can filter out events that are the direct result of an invocation
    \*: If the event is the result of a received invocation the `id` should be the `id` of the invocation otherwise it should be `null`. This is so clients can filter out events that are the direct result of an invocation


    # Notes
  9. SReject revised this gist Jun 23, 2023. 1 changed file with 8 additions and 8 deletions.
    16 changes: 8 additions & 8 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -60,14 +60,14 @@ Invoke messages that result in a state change that would generate an event *shou
    ### Event
    An event-message is used to notify the recipient of a state change. Event-messages infer the sender does not care about the actions taken by recipient as a result of receiving the message.

    | Property | Type | Nullable | Description |
    |----------|:---------:|:--------:|------------------------------------|
    | `type` | `"event"` | no | Literal text `event` |
    | `id`\* | `string` | yes | Ignored |
    | `name` | `string` | no | Event name/Notification identifier |
    | `data` | *any* | yes | The data accompanying the event |

    \*: To keep the formats consistent, the `id` parameter must be specified but its value is ignored by the recipient.
    | Property | Type | Nullable | Description |
    |----------|:--------------------:|:--------:|------------------------------------|
    | `type` | `"event"` | no | Literal text `event` |
    | `id`\* | `number` \| `string` | yes | Ignored |
    | `name` | `string` | no | Event name/Notification identifier |
    | `data` | *any* | yes | The data accompanying the event |

    \*: If the event is the result of a received invocation the `id` should be the `id` of the invocation only for the client that issued the invocation, otherwise it should be `null`. This is so clients can filter out events that are the direct result of an invocation


    # Notes
  10. SReject revised this gist Jun 22, 2023. 1 changed file with 16 additions and 3 deletions.
    19 changes: 16 additions & 3 deletions RRE Example.md
    Original file line number Diff line number Diff line change
    @@ -1,57 +1,70 @@
    ### Example Communications

    ```

    Client sends a 'authorize' invocation to server:
    ```js
    {
    "type": "invoke",
    "id": "authMe",
    "name": "authorize",
    "data": ["guest"]
    }
    ```

    [Server begins processing the authorize innvocation]
    Server begins processing the authorize innvocation

    Client sends an 'add' invocation before server finishes processing the authorize invocation:
    ```js
    {
    "type": "invoke",
    "id": "add2and3",
    "name": "add",
    "data": [2, 3]
    }
    ```

    Server Responds to 'add' invocation (authorize innvocation still pending):
    Server Responds to 'add' invocation (authorize invocation still pending):
    ```js
    {
    "type": "response",
    "id": "add2and3",
    "name": "error",
    "data": "not authorized"
    }
    ```

    Server finishes authorizing user:
    ```js
    {
    "type": "response",
    "id": "authMe",
    "name": "ok",
    "data": null
    }
    ```

    Server broadcasts a message to all clients:
    ```js
    {
    "type": "event",
    "id": null,
    "name": "userConnect",
    "data": {"type": "guest"}
    }
    ```

    Client resends 'add' invocation:
    ```js
    {
    "type": "invoke",
    "id": "add2and3again",
    "name": "add",
    "data": [2, 3]
    }
    ```

    Server processes the 'add' invocation and responds:
    ```js
    {
    "type": "response",
    "id": "add2and3again",
  11. SReject revised this gist Jun 22, 2023. 2 changed files with 22 additions and 22 deletions.
    42 changes: 21 additions & 21 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    # Invoke, Response, Event
    The goal of the Invoke, Response, Event protocol is to provide a generic meaningful format for communications while providing mechanisms related to querying resources and transmit state change notifications.
    The goal of the Invoke, Response, Event protocol is to provide a generic meaningful format for communications while providing mechanisms related to querying resources and transmiting state change notifications.

    ## Message Formats
    Messages are transmitted as valid JSON-parsable strings and must conform to one of the message-formats given below.
    @@ -18,12 +18,12 @@ Invoke messages are used to query or update a remote resource. These messages mu
    Invoke messages may be processed in any order regardless of the order in which they were received; this is to allow for parallel and asynchronous processing of messages.


    | Property | Type | Nullable | Description |
    |------------|:------------:|:--------:|-------------------------------------------------------------|
    | `type` | `"invoke" ` | no | Literal text `invoke` |
    | `id`\* | `String` | no | A sender-generated ID |
    | `name` | `String` | no | The resource identifier to query |
    | `data`\** | `Array<any>` | no | A list of values(arguments) to pass to the resource handler |
    | Property | Type | Nullable | Description |
    |------------|:--------------------:|:--------:|-------------------------------------------------------------|
    | `type` | `"invoke" ` | no | Literal text `invoke` |
    | `id`\* | `number` \| `string` | no | A sender-generated ID |
    | `name` | `string` | no | The resource identifier to query |
    | `data`\** | `Array<any>` | no | A list of values(arguments) to pass to the resource handler |

    \*: The `id` must be unique to other pending innvocations but may be reused once a response has been received.
    \*\*: If no values are to be provided to the resource `data` should be an empty array.
    @@ -38,22 +38,22 @@ Invoke messages that result in a state change that would generate an event *shou
    <br>

    #### Success
    | Property | Type | Nullable | Description |
    |----------|:------------:|:--------:|----------------------------------------------------|
    | `type` | `"response"` | no | Literal text `response` |
    | `id` | `String` | no | The `id` property received with the invoke-message |
    | `name` | `"success"` | no | Literal text `success` |
    | `data` | *any* | yes | Resulting value from processing the query |
    | Property | Type | Nullable | Description |
    |----------|:--------------------:|:--------:|----------------------------------------------------|
    | `type` | `"response"` | no | Literal text `response` |
    | `id` | `number` \| `string` | no | The `id` property received with the invoke-message |
    | `name` | `"success"` | no | Literal text `success` |
    | `data` | *any* | yes | Resulting value from processing the query |

    <br>

    #### Error
    | Property | Type | Nullable | Description |
    |----------|:------------:|:--------:|----------------------------------------------------|
    | `type` | `"response"` | no | Literal text `response` |
    | `id` | `String` | no | The `id` property received with the invoke-message |
    | `name` | `"error"` | no | Literal text `error` |
    | `data` | `String` | yes | Error message detailing why the query failed |
    | Property | Type | Nullable | Description |
    |----------|:--------------------:|:--------:|-------------------------------------------------------|
    | `type` | `"response"` | no | Literal text `response` |
    | `id` | `number` \| `string` | no | The `id` property received with the invoke-message |
    | `name` | `"error"` | no | Literal text `error` |
    | `data` | *any* | yes | Error information detailing why the invocation failed |

    <br>

    @@ -63,8 +63,8 @@ An event-message is used to notify the recipient of a state change. Event-messag
    | Property | Type | Nullable | Description |
    |----------|:---------:|:--------:|------------------------------------|
    | `type` | `"event"` | no | Literal text `event` |
    | `id`\* | `String` | no | Ignored |
    | `name` | `String` | no | Event name/Notification identifier |
    | `id`\* | `string` | yes | Ignored |
    | `name` | `string` | no | Event name/Notification identifier |
    | `data` | *any* | yes | The data accompanying the event |

    \*: To keep the formats consistent, the `id` parameter must be specified but its value is ignored by the recipient.
    2 changes: 1 addition & 1 deletion RRE Example.md
    Original file line number Diff line number Diff line change
    @@ -38,7 +38,7 @@ Server finishes authorizing user:
    Server broadcasts a message to all clients:
    {
    "type": "event",
    "id": "",
    "id": null,
    "name": "userConnect",
    "data": {"type": "guest"}
    }
  12. SReject revised this gist Jun 22, 2023. 1 changed file with 9 additions and 9 deletions.
    18 changes: 9 additions & 9 deletions RRE Example.md
    Original file line number Diff line number Diff line change
    @@ -1,25 +1,25 @@
    ### Example Communications

    ```
    Client sends a 'authorize' request to server:
    Client sends a 'authorize' invocation to server:
    {
    "type": "request",
    "type": "invoke",
    "id": "authMe",
    "name": "authorize",
    "data": ["guest"]
    }
    [Server begins processing the authorize request]
    [Server begins processing the authorize innvocation]
    Client sends an 'add' request before server finishes processing the authorize request:
    Client sends an 'add' invocation before server finishes processing the authorize invocation:
    {
    "type": "request",
    "type": "invoke",
    "id": "add2and3",
    "name": "add",
    "data": [2, 3]
    }
    Server Responds to 'add' request (authorize request still pending):
    Server Responds to 'add' invocation (authorize innvocation still pending):
    {
    "type": "response",
    "id": "add2and3",
    @@ -43,15 +43,15 @@ Server broadcasts a message to all clients:
    "data": {"type": "guest"}
    }
    Client resends 'add' request:
    Client resends 'add' invocation:
    {
    "type": "request",
    "type": "invoke",
    "id": "add2and3again",
    "name": "add",
    "data": [2, 3]
    }
    Server processes the 'add' request and responds:
    Server processes the 'add' invocation and responds:
    {
    "type": "response",
    "id": "add2and3again",
  13. SReject revised this gist Jun 22, 2023. 1 changed file with 17 additions and 17 deletions.
    34 changes: 17 additions & 17 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -13,47 +13,47 @@ Messages that are non-conformant may be treated as invalid, discarded, or allowe
    <br>

    ### Invoke
    Invoke messages are used to query a remote resource. These messages must always result in a [`Response`](#reponse) from the recipient. Invoke messages that result in a state change that would generate an event *should* generate the [`Event`](#event) as well as the [`Response`](#response)
    Invoke messages are used to query or update a remote resource. These messages must always result in a [`Response`](#reponse) from the recipient.

    Invoke messages may be processed in any order regardless of the order in which they were received; this is to allow for parallel and asynchronous processing of messages.


    | Property | Type | Nullable | Description |
    |------------|:------------:|:--------:|-------------------------------------------------------------|
    | `type` | `"request"` | no | Literal text `request` |
    | `type` | `"invoke" ` | no | Literal text `invoke` |
    | `id`\* | `String` | no | A sender-generated ID |
    | `name` | `String` | no | The resource identifier to query |
    | `data`\** | `Array<any>` | no | A list of values(arguments) to pass to the resource handler |

    \*: The `id` must be unique to other pending requests but may be reused once a response has been received.
    \*: The `id` must be unique to other pending innvocations but may be reused once a response has been received.
    \*\*: If no values are to be provided to the resource `data` should be an empty array.

    <br>

    ### Response
    Upon receiving a [`Request`](#request), the recipiant should process the request and respond with a response-message. Responses may be transmitted in any order regardless of order in which the originating requests were received.
    Upon receiving a [`Invoke`](#invoke) message, the recipiant should process the innvocation and respond with a response-message. Responses may be transmitted in any order regardless of order in which the originating requests were received.

    Invoke messages that result in a state change that would generate an event *should* generate the [`Event`](#event) as well as the [`Response`](#response)

    <br>

    #### Success
    | Property | Type | Nullable | Description |
    |----------|:------------:|:--------:|-----------------------------------------------------|
    | `type` | `"response"` | no | Literal text `response` |
    | `id` | `String` | no | The `id` property received with the request-message |
    | `name` | `"success"` | no | Literal text `success` |
    | `data` | *any* | yes | Resulting value from processing the query |
    | Property | Type | Nullable | Description |
    |----------|:------------:|:--------:|----------------------------------------------------|
    | `type` | `"response"` | no | Literal text `response` |
    | `id` | `String` | no | The `id` property received with the invoke-message |
    | `name` | `"success"` | no | Literal text `success` |
    | `data` | *any* | yes | Resulting value from processing the query |

    <br>

    #### Error
    | Property | Type | Nullable | Description |
    |----------|:------------:|:--------:|-----------------------------------------------------|
    | `type` | `"response"` | no | Literal text `response` |
    | `id` | `String` | no | The `id` property received with the request-message |
    | `name` | `"error"` | no | Literal text `error` |
    | `data` | `String` | yes | Error message detailing why the query failed |
    | Property | Type | Nullable | Description |
    |----------|:------------:|:--------:|----------------------------------------------------|
    | `type` | `"response"` | no | Literal text `response` |
    | `id` | `String` | no | The `id` property received with the invoke-message |
    | `name` | `"error"` | no | Literal text `error` |
    | `data` | `String` | yes | Error message detailing why the query failed |

    <br>

    @@ -71,4 +71,4 @@ An event-message is used to notify the recipient of a state change. Event-messag


    # Notes
    This is based heavily on [jsonrpc](https://www.jsonrpc.org/specification) with the caveat of keeping a strict structure to all messages
    This is heavily inspired by [jsonrpc](https://www.jsonrpc.org/specification) with a focus on the format and ignoring implementation.
  14. SReject revised this gist Jun 22, 2023. 2 changed files with 43 additions and 40 deletions.
    69 changes: 36 additions & 33 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,68 +1,71 @@
    # Request, Response, Event
    The goal of the Request, Response, Event protocol is to provide a generic meaningful format for communications while providing mechanisms related to querying resources and state change notifications.
    # Invoke, Response, Event
    The goal of the Invoke, Response, Event protocol is to provide a generic meaningful format for communications while providing mechanisms related to querying resources and transmit state change notifications.

    ## Message Formats
    Messages are transmitted as valid JSON-parsable strings and must conform to one of the message-formats given below.

    All format-defined properties are required to be part of the resulting JSON string. If a defined property does not have an applicable value, it's value should default to `null`.

    Implementations should **NOT** define/rely upon any property not defined in a format. Any non format-defined properties in the message's JSON string are to be ignored by the recipient.
    Implementations should **NOT** define or rely upon any property not defined in this format. Any non format-defined properties in the message's JSON string are to be ignored by the recipient.

    Messages that are non-conformant may be treated as invalid, discarded/ignored, or allowed to passed through.
    Messages that are non-conformant may be treated as invalid, discarded, or allowed to passed through.

    <br>

    ### Request
    Request-messages are used to query a remote resource. These messages must always result in a [`Response`](#reponse) from the recipient.
    ### Invoke
    Invoke messages are used to query a remote resource. These messages must always result in a [`Response`](#reponse) from the recipient. Invoke messages that result in a state change that would generate an event *should* generate the [`Event`](#event) as well as the [`Response`](#response)

    Requests may be processed in any order regardless of the order in which they were received; this is to allow for parallel and asynchronous processing of messages.
    Invoke messages may be processed in any order regardless of the order in which they were received; this is to allow for parallel and asynchronous processing of messages.

    | Property | Type | Nullable | Description |
    |------------|:------------------:|:--------:|-------------------------------------------------------------|
    | `type` | `String="request"` | no | Literal text `request` |
    | `id`\* | `String` | no | A sender-generated ID |
    | `meta` | `String` | no | The resource identifier to query |
    | `data`\** | `Array<any>` | no | A list of values(arguments) to pass to the resource handler |

    \*: The `id` should be unique to the connection session but may be reused once a response has been received.
    | Property | Type | Nullable | Description |
    |------------|:------------:|:--------:|-------------------------------------------------------------|
    | `type` | `"request"` | no | Literal text `request` |
    | `id`\* | `String` | no | A sender-generated ID |
    | `name` | `String` | no | The resource identifier to query |
    | `data`\** | `Array<any>` | no | A list of values(arguments) to pass to the resource handler |

    \*: The `id` must be unique to other pending requests but may be reused once a response has been received.
    \*\*: If no values are to be provided to the resource `data` should be an empty array.

    <br>

    ### Response
    Upon receiving a [`Request`](#request), the recipiant should process the request and respond with a response-message. Responses may be transmitted in any order regardless of order in which the originating requests were received.
    Upon receiving a [`Request`](#request), the recipiant should process the request and respond with a response-message. Responses may be transmitted in any order regardless of order in which the originating requests were received.

    Invoke messages that result in a state change that would generate an event *should* generate the [`Event`](#event) as well as the [`Response`](#response)

    <br>

    #### Success
    | Property | Type | Nullable | Description |
    |----------|:-------------------:|:--------:|-----------------------------------------------------|
    | `type` | `String="response"` | no | Literal text `response` |
    | `id` | `String` | no | The `id` property received with the request-message |
    | `meta` | `String="ok"` | no | Literal text `ok` |
    | `data` | *any* | yes | Resulting value from processing the query |
    | Property | Type | Nullable | Description |
    |----------|:------------:|:--------:|-----------------------------------------------------|
    | `type` | `"response"` | no | Literal text `response` |
    | `id` | `String` | no | The `id` property received with the request-message |
    | `name` | `"success"` | no | Literal text `success` |
    | `data` | *any* | yes | Resulting value from processing the query |

    <br>

    #### Error
    | Property | Type | Nullable | Description |
    |----------|:-------------------:|:--------:|-----------------------------------------------------|
    | `type` | `String="response"` | no | Literal text `response` |
    | `id` | `String` | no | The `id` property received with the request-message |
    | `meta` | `String="error"` | no | Literal text `error` |
    | `data` | `String` | yes | Error message detailing why the query failed |
    | Property | Type | Nullable | Description |
    |----------|:------------:|:--------:|-----------------------------------------------------|
    | `type` | `"response"` | no | Literal text `response` |
    | `id` | `String` | no | The `id` property received with the request-message |
    | `name` | `"error"` | no | Literal text `error` |
    | `data` | `String` | yes | Error message detailing why the query failed |

    <br>

    ### Event
    An event-message is used to notify the recipient of a state change. Event-messages infer the sender does not care about the actions taken by recipient as a result of receiving the message.

    | Property | Type | Nullable | Description |
    |----------|:----------------:|:--------:|------------------------------------|
    | `type` | `String="event"` | no | Literal text `event` |
    | `id`\* | `String` | no | Ignored |
    | `meta` | `String` | no | Event name/Notification identifier |
    | `data` | *any* | yes | The data accompanying the event |
    | Property | Type | Nullable | Description |
    |----------|:---------:|:--------:|------------------------------------|
    | `type` | `"event"` | no | Literal text `event` |
    | `id`\* | `String` | no | Ignored |
    | `name` | `String` | no | Event name/Notification identifier |
    | `data` | *any* | yes | The data accompanying the event |

    \*: To keep the formats consistent, the `id` parameter must be specified but its value is ignored by the recipient.

    14 changes: 7 additions & 7 deletions RRE Example.md
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@ Client sends a 'authorize' request to server:
    {
    "type": "request",
    "id": "authMe",
    "meta": "authorize",
    "name": "authorize",
    "data": ["guest"]
    }
    @@ -15,47 +15,47 @@ Client sends an 'add' request before server finishes processing the authorize re
    {
    "type": "request",
    "id": "add2and3",
    "meta": "add",
    "name": "add",
    "data": [2, 3]
    }
    Server Responds to 'add' request (authorize request still pending):
    {
    "type": "response",
    "id": "add2and3",
    "meta": "error",
    "name": "error",
    "data": "not authorized"
    }
    Server finishes authorizing user:
    {
    "type": "response",
    "id": "authMe",
    "meta": "ok",
    "name": "ok",
    "data": null
    }
    Server broadcasts a message to all clients:
    {
    "type": "event",
    "id": "",
    "meta": "userConnect",
    "name": "userConnect",
    "data": {"type": "guest"}
    }
    Client resends 'add' request:
    {
    "type": "request",
    "id": "add2and3again",
    "meta": "add",
    "name": "add",
    "data": [2, 3]
    }
    Server processes the 'add' request and responds:
    {
    "type": "response",
    "id": "add2and3again",
    "meta": "ok",
    "name": "ok",
    "data": 5
    }
    ```
  15. SReject revised this gist Feb 6, 2019. 1 changed file with 6 additions and 6 deletions.
    12 changes: 6 additions & 6 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -57,12 +57,12 @@ Upon receiving a [`Request`](#request), the recipiant should process the request
    ### Event
    An event-message is used to notify the recipient of a state change. Event-messages infer the sender does not care about the actions taken by recipient as a result of receiving the message.

    | Property | Type | Nullable | Description |
    |----------|:---------------:|:--------:|------------------------------------|
    | `type` | `String="event` | no | Literal text "event" |
    | `id`\* | `String` | no | Ignored |
    | `meta` | `String` | no | Event name/Notification identifier |
    | `data` | *any* | yes | The data accompanying the event |
    | Property | Type | Nullable | Description |
    |----------|:----------------:|:--------:|------------------------------------|
    | `type` | `String="event"` | no | Literal text `event` |
    | `id`\* | `String` | no | Ignored |
    | `meta` | `String` | no | Event name/Notification identifier |
    | `data` | *any* | yes | The data accompanying the event |

    \*: To keep the formats consistent, the `id` parameter must be specified but its value is ignored by the recipient.

  16. SReject revised this gist Jan 30, 2019. 1 changed file with 5 additions and 1 deletion.
    6 changes: 5 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -64,4 +64,8 @@ An event-message is used to notify the recipient of a state change. Event-messag
    | `meta` | `String` | no | Event name/Notification identifier |
    | `data` | *any* | yes | The data accompanying the event |

    \*: To keep the formats consistent, the `id` parameter must be specified but its value is ignored by the recipient.
    \*: To keep the formats consistent, the `id` parameter must be specified but its value is ignored by the recipient.


    # Notes
    This is based heavily on [jsonrpc](https://www.jsonrpc.org/specification) with the caveat of keeping a strict structure to all messages
  17. SReject revised this gist Jan 19, 2019. 1 changed file with 9 additions and 9 deletions.
    18 changes: 9 additions & 9 deletions RRE Example.md
    Original file line number Diff line number Diff line change
    @@ -1,57 +1,57 @@
    ### Example Communications

    ```
    Client - Registering as a guest with server
    Client sends a 'authorize' request to server:
    {
    "type": "request",
    "id": "authMe",
    "meta": "authorize",
    "data": ["guest"]
    }
    Server - begins processing the authorize request
    [Server begins processing the authorize request]
    Client - Attempts to send add request before server finishes process the authorize request
    Client sends an 'add' request before server finishes processing the authorize request:
    {
    "type": "request",
    "id": "add2and3",
    "meta": "add",
    "data": [2, 3]
    }
    Server - Responds to add request (authorize request still pending)
    Server Responds to 'add' request (authorize request still pending):
    {
    "type": "response",
    "id": "add2and3",
    "meta": "error",
    "data": "Not registered"
    "data": "not authorized"
    }
    Server - Finishes authorizing user
    Server finishes authorizing user:
    {
    "type": "response",
    "id": "authMe",
    "meta": "ok",
    "data": null
    }
    Server - broadcasts a message to all clients
    Server broadcasts a message to all clients:
    {
    "type": "event",
    "id": "",
    "meta": "userConnect",
    "data": {"type": "guest"}
    }
    Client - Resends add request
    Client resends 'add' request:
    {
    "type": "request",
    "id": "add2and3again",
    "meta": "add",
    "data": [2, 3]
    }
    Server - Processes add request and responds
    Server processes the 'add' request and responds:
    {
    "type": "response",
    "id": "add2and3again",
  18. SReject revised this gist Jan 19, 2019. 1 changed file with 5 additions and 3 deletions.
    8 changes: 5 additions & 3 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    # Request, Response, Event
    The goal of a Request, Response, Event protocol is to give all messages a consistent meaningful format while defining mechanisms for remote querying.
    The goal of the Request, Response, Event protocol is to provide a generic meaningful format for communications while providing mechanisms related to querying resources and state change notifications.

    ## Message Formats
    Messages are transmitted as valid JSON-parsable strings and must conform to one of the message-formats given below.
    @@ -8,12 +8,14 @@ All format-defined properties are required to be part of the resulting JSON stri

    Implementations should **NOT** define/rely upon any property not defined in a format. Any non format-defined properties in the message's JSON string are to be ignored by the recipient.

    Messages that are nonconformant may be treated as invalid, discarded/ignored, or allowed to passed through.
    Messages that are non-conformant may be treated as invalid, discarded/ignored, or allowed to passed through.

    <br>

    ### Request
    A request-message is used by the sender to query a remote resource. These messages must always result in a [`Reply`](#reply).
    Request-messages are used to query a remote resource. These messages must always result in a [`Response`](#reponse) from the recipient.

    Requests may be processed in any order regardless of the order in which they were received; this is to allow for parallel and asynchronous processing of messages.

    | Property | Type | Nullable | Description |
    |------------|:------------------:|:--------:|-------------------------------------------------------------|
  19. SReject revised this gist Jan 19, 2019. 1 changed file with 61 additions and 0 deletions.
    61 changes: 61 additions & 0 deletions RRE Example.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,61 @@
    ### Example Communications

    ```
    Client - Registering as a guest with server
    {
    "type": "request",
    "id": "authMe",
    "meta": "authorize",
    "data": ["guest"]
    }
    Server - begins processing the authorize request
    Client - Attempts to send add request before server finishes process the authorize request
    {
    "type": "request",
    "id": "add2and3",
    "meta": "add",
    "data": [2, 3]
    }
    Server - Responds to add request (authorize request still pending)
    {
    "type": "response",
    "id": "add2and3",
    "meta": "error",
    "data": "Not registered"
    }
    Server - Finishes authorizing user
    {
    "type": "response",
    "id": "authMe",
    "meta": "ok",
    "data": null
    }
    Server - broadcasts a message to all clients
    {
    "type": "event",
    "id": "",
    "meta": "userConnect",
    "data": {"type": "guest"}
    }
    Client - Resends add request
    {
    "type": "request",
    "id": "add2and3again",
    "meta": "add",
    "data": [2, 3]
    }
    Server - Processes add request and responds
    {
    "type": "response",
    "id": "add2and3again",
    "meta": "ok",
    "data": 5
    }
    ```
  20. SReject revised this gist Jan 19, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@ Messages are transmitted as valid JSON-parsable strings and must conform to one

    All format-defined properties are required to be part of the resulting JSON string. If a defined property does not have an applicable value, it's value should default to `null`.

    Implementations should **NOT** define/relay upon any property not defined in a format. Any non format-defined property in the message's JSON string is to be ignored by the recipient.
    Implementations should **NOT** define/rely upon any property not defined in a format. Any non format-defined properties in the message's JSON string are to be ignored by the recipient.

    Messages that are nonconformant may be treated as invalid, discarded/ignored, or allowed to passed through.

  21. SReject revised this gist Jan 19, 2019. 1 changed file with 15 additions and 6 deletions.
    21 changes: 15 additions & 6 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -8,7 +8,9 @@ All format-defined properties are required to be part of the resulting JSON stri

    Implementations should **NOT** define/relay upon any property not defined in a format. Any non format-defined property in the message's JSON string is to be ignored by the recipient.

    Messages that are nonconformant may be treated as invalid, discarded/ignored, or allowed to passed through.
    Messages that are nonconformant may be treated as invalid, discarded/ignored, or allowed to passed through.

    <br>

    ### Request
    A request-message is used by the sender to query a remote resource. These messages must always result in a [`Reply`](#reply).
    @@ -23,25 +25,32 @@ A request-message is used by the sender to query a remote resource. These messag
    \*: The `id` should be unique to the connection session but may be reused once a response has been received.
    \*\*: If no values are to be provided to the resource `data` should be an empty array.

    <br>

    ### Response
    Upon receiving a [`Request`](#request), the recipiant should process the request and respond with a response-message. Responses may be transmitted in any order regardless of order in which the originating requests were received.
    Upon receiving a [`Request`](#request), the recipiant should process the request and respond with a response-message. Responses may be transmitted in any order regardless of order in which the originating requests were received.

    ##### Success
    <br>

    #### Success
    | Property | Type | Nullable | Description |
    |----------|:-------------------:|:--------:|-----------------------------------------------------|
    | `type` | `String="response"` | no | Literal text `response` |
    | `id` | `String` | no | The `id` property received with the request-message |
    | `meta` | `String="ok"` | no | Literal text `ok` |
    | `data` | *any* | yes | Resulting value from processing the query |
    | `data` | *any* | yes | Resulting value from processing the query |

    <br>

    ##### Error
    #### Error
    | Property | Type | Nullable | Description |
    |----------|:-------------------:|:--------:|-----------------------------------------------------|
    | `type` | `String="response"` | no | Literal text `response` |
    | `id` | `String` | no | The `id` property received with the request-message |
    | `meta` | `String="error"` | no | Literal text `error` |
    | `data` | `String` | yes | Error message detailing why the query failed |
    | `data` | `String` | yes | Error message detailing why the query failed |

    <br>

    ### Event
    An event-message is used to notify the recipient of a state change. Event-messages infer the sender does not care about the actions taken by recipient as a result of receiving the message.
  22. SReject created this gist Jan 19, 2019.
    56 changes: 56 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,56 @@
    # Request, Response, Event
    The goal of a Request, Response, Event protocol is to give all messages a consistent meaningful format while defining mechanisms for remote querying.

    ## Message Formats
    Messages are transmitted as valid JSON-parsable strings and must conform to one of the message-formats given below.

    All format-defined properties are required to be part of the resulting JSON string. If a defined property does not have an applicable value, it's value should default to `null`.

    Implementations should **NOT** define/relay upon any property not defined in a format. Any non format-defined property in the message's JSON string is to be ignored by the recipient.

    Messages that are nonconformant may be treated as invalid, discarded/ignored, or allowed to passed through.

    ### Request
    A request-message is used by the sender to query a remote resource. These messages must always result in a [`Reply`](#reply).

    | Property | Type | Nullable | Description |
    |------------|:------------------:|:--------:|-------------------------------------------------------------|
    | `type` | `String="request"` | no | Literal text `request` |
    | `id`\* | `String` | no | A sender-generated ID |
    | `meta` | `String` | no | The resource identifier to query |
    | `data`\** | `Array<any>` | no | A list of values(arguments) to pass to the resource handler |

    \*: The `id` should be unique to the connection session but may be reused once a response has been received.
    \*\*: If no values are to be provided to the resource `data` should be an empty array.

    ### Response
    Upon receiving a [`Request`](#request), the recipiant should process the request and respond with a response-message. Responses may be transmitted in any order regardless of order in which the originating requests were received.

    ##### Success
    | Property | Type | Nullable | Description |
    |----------|:-------------------:|:--------:|-----------------------------------------------------|
    | `type` | `String="response"` | no | Literal text `response` |
    | `id` | `String` | no | The `id` property received with the request-message |
    | `meta` | `String="ok"` | no | Literal text `ok` |
    | `data` | *any* | yes | Resulting value from processing the query |

    ##### Error
    | Property | Type | Nullable | Description |
    |----------|:-------------------:|:--------:|-----------------------------------------------------|
    | `type` | `String="response"` | no | Literal text `response` |
    | `id` | `String` | no | The `id` property received with the request-message |
    | `meta` | `String="error"` | no | Literal text `error` |
    | `data` | `String` | yes | Error message detailing why the query failed |


    ### Event
    An event-message is used to notify the recipient of a state change. Event-messages infer the sender does not care about the actions taken by recipient as a result of receiving the message.

    | Property | Type | Nullable | Description |
    |----------|:---------------:|:--------:|------------------------------------|
    | `type` | `String="event` | no | Literal text "event" |
    | `id`\* | `String` | no | Ignored |
    | `meta` | `String` | no | Event name/Notification identifier |
    | `data` | *any* | yes | The data accompanying the event |

    \*: To keep the formats consistent, the `id` parameter must be specified but its value is ignored by the recipient.