{"version":3,"file":"index.mjs","names":["EngineIoServer"],"sources":["../composer.ts","../index.ts"],"sourcesContent":["import { Map } from 'immutable'\n\nconst noop: NextFunc = async () => {}\n\nexport type NextFunc = (data: Map<string, any>) => Promise<unknown>\n\nexport type ComposableFunc = (data: Map<string, any>, next: NextFunc) => Promise<unknown>\n\nexport default function compose(fns: ComposableFunc[]): NextFunc {\n  let next = noop\n  let i = fns.length\n  while (i--) {\n    const fn = fns[i]\n    const curNext = next\n    next = data => fn(data, curNext)\n  }\n\n  return async (data: Map<any, any>) => next(data)\n}\n","import {\n  AttachOptions,\n  Server as EngineIoServer,\n  ServerOptions as EngineIoServerOptions,\n  Socket,\n} from 'engine.io'\nimport { EventEmitter } from 'events'\nimport http, { STATUS_CODES } from 'http'\nimport { fromJS, Map, Set } from 'immutable'\nimport { nanoid } from 'nanoid'\nimport { decode, encode, MessageType, NydusInvokeMessage, protocolVersion } from 'nydus-protocol'\nimport ruta, { Router } from 'ruta3'\nimport type { ComposableFunc, NextFunc } from './composer.js'\nimport compose from './composer.js'\n\nexport { protocolVersion }\n\nconst PACKAGE_ONLY = Symbol('nydus-package-only')\n\ntype NydusClientEvents = {\n  /** Fired when the client has disconnected. */\n  close: [reason: string, description?: Error]\n  /** Fired when a general error occurs. */\n  error: [err: Error]\n}\n\n/** A client that is connected to the server. */\nexport class NydusClient extends EventEmitter<NydusClientEvents> {\n  subscriptions: Set<string>\n\n  constructor(\n    readonly id: string,\n    readonly conn: Socket,\n    private onInvoke: (client: NydusClient, message: NydusInvokeMessage<unknown>) => void,\n    private onClose: (client: NydusClient) => void,\n    private onParserError: (client: NydusClient, msg: string) => void,\n  ) {\n    super()\n    this.subscriptions = Set()\n\n    conn\n      .on('error', err => this.emit('error', err))\n      .on('close', this.handleClose.bind(this))\n      .on('message', msg => this.onMessage(msg as string))\n  }\n\n  /** Returns the current `readyState` of the connection. */\n  get readyState() {\n    return this.conn.readyState\n  }\n\n  equals(other: unknown) {\n    return other === this || (other instanceof NydusClient && other.id === this.id)\n  }\n\n  // This is taken from immutable's String hashing code\n  hashCode(): number {\n    const string = this.id\n    let hashed = 0\n    for (let ii = 0; ii < string.length; ii++) {\n      hashed = (31 * hashed + string.charCodeAt(ii)) | 0\n    }\n    return ((hashed >>> 1) & 0x40000000) | (hashed & 0xbfffffff)\n  }\n\n  /** Closes the underlying connection. */\n  close() {\n    this.conn.close()\n  }\n\n  send(packageOnlyKey: typeof PACKAGE_ONLY, encoded: string) {\n    if (packageOnlyKey === PACKAGE_ONLY) {\n      this.conn.send(encoded, undefined)\n    }\n  }\n\n  private onMessage(msg: string) {\n    const decoded = decode(msg)\n    switch (decoded.type) {\n      case MessageType.ParserError:\n        this.onParserError(this, msg)\n        this.conn.close() // will cause a call to onClose\n        break\n      case MessageType.Invoke:\n        this.onInvoke(this, decoded)\n        break\n    }\n  }\n\n  private handleClose(reason: string, description?: Error) {\n    this.onClose(this)\n    this.emit('close', reason, description)\n  }\n}\n\nexport class InvokeError extends Error {\n  readonly status: number\n  readonly body: any\n\n  constructor(message: string, status: number, body?: any) {\n    super(message)\n    this.status = status\n    this.body = body\n  }\n}\n\nfunction isInvokeError(err: Error): err is InvokeError {\n  return typeof (err as any).status === 'number'\n}\n\nfunction defaultErrorConverter(err: Error): unknown {\n  const isDev = process.env.NODE_ENV !== 'production'\n  let status = 500\n  let message = STATUS_CODES[500]\n  let body: any | undefined\n\n  if (isInvokeError(err)) {\n    status = err.status\n    message = err.message\n    body = err.body\n  } else if (isDev) {\n    message = err.message\n    body ??= err.stack\n  }\n\n  message ??= STATUS_CODES[status]\n\n  return { message, status, body }\n}\n\nconst NOT_FOUND = { message: 'Not Found', status: 404 }\n\nexport interface NydusServerOptions extends EngineIoServerOptions, AttachOptions {\n  /**\n   * A function to convert errors to a sanitized version before sending them to clients. Optional,\n   * the default implementation passes a status code, message, and body if present. In development\n   * mode, it will include the stack trace as the body if there isn't a body already.\n   */\n  invokeErrorConverter: (err: Error, client: NydusClient) => unknown\n}\n\nexport type RouteHandler = ComposableFunc\nexport { NextFunc }\n\ntype NydusServerEvents = {\n  /** Fired when a new client has connected. */\n  connection: [client: NydusClient]\n  /** Fired when a general error occurs. */\n  error: [err: Error]\n  /** Fired when a client receives a message it could not parse. */\n  parserError: [client: NydusClient, msg: string]\n  /**\n   * Fired when an invoke throws an error that gets converted to a 500 (e.g. an internal server\n   * error).\n   */\n  invokeError: [err: Error, client: NydusClient, msg: NydusInvokeMessage<unknown>]\n}\n\nexport class NydusServer extends EventEmitter<NydusServerEvents> {\n  static readonly protocolVersion = protocolVersion\n\n  /** A map of client ID -> client. */\n  clients: Map<string, NydusClient>\n\n  private eioServer: EngineIoServer\n  private invokeErrorConverter: (err: Error, client: NydusClient) => unknown\n  private idGen: () => string\n  private subscriptions: Map<string, Set<NydusClient>>\n  private router: Router<NextFunc>\n\n  private onInvokeFunc = this.onInvoke.bind(this)\n  private onCloseFunc = this.onDisconnect.bind(this)\n  private onParserErrorFunc = this.onParserError.bind(this)\n\n  constructor(options: Partial<NydusServerOptions> = {}) {\n    super()\n    this.eioServer = new EngineIoServer(options)\n    this.invokeErrorConverter = options.invokeErrorConverter ?? defaultErrorConverter\n    this.idGen = nanoid\n    this.clients = Map()\n    this.subscriptions = Map()\n    this.router = ruta()\n    ;(this.eioServer as EventEmitter)\n      .on('error', err => this.emit('error', err))\n      .on('connection', this.onConnection.bind(this))\n  }\n\n  /**\n   * Attach this NydusServer to a particular HTTP(S) server, making it listen to UPGRADE requests.\n   */\n  attach(httpServer: http.Server, options: AttachOptions) {\n    this.eioServer.attach(httpServer, options)\n  }\n\n  /** Close any open connections and then stop this Nydus server. */\n  close() {\n    for (const client of this.clients.values()) {\n      client.close()\n    }\n    this.clients = this.clients.clear()\n    this.eioServer.close()\n  }\n\n  /**\n   * Set the function used to generate IDs for messages. Should return a string of 32 characters or\n   * less, matching /[A-z0-9-]+/.\n   *\n   * This is mainly useful for testing, generally this shouldn't need to be used.\n   */\n  setIdGen(idGenFunc: () => string) {\n    this.idGen = idGenFunc\n  }\n\n  /**\n   * Registers one or more handlers to respond to INVOKEs on paths matching a pattern. Handlers are\n   * async functions (and thus return promises when called) of the form:\n   *   `(data, next) => Promise<any>`\n   *\n   * Handlers will be composed in order, and are expected to call next(data, next) to make execution\n   * continue further down the chain. Data is an immutable map that can be changed before passing it\n   * to the next function, but should present the same API (or ideally, be an ImmutableJS map) for\n   * compatibility with other handlers.\n   *\n   * The final resolved value will be sent to the client (as a RESULT if the promise was\n   * successfully resolved, an ERROR if it was rejected).\n   */\n  registerRoute(pathPattern: string, ...handlers: RouteHandler[]) {\n    if (!handlers.length) {\n      throw new Error('At least one handler function is required')\n    }\n\n    this.router.addRoute(pathPattern, compose(handlers))\n  }\n\n  /**\n   * Add a subscription to a publish path for a client. Whenever messages are published to that\n   * path, this client will receive a message (until unsubscribed). If initialData is specified and\n   * the client was not previously subscribed, this data will be published to the client\n   * immediately (but not to the other subscribed clients).\n   *\n   * @param client the NydusClient to subscribe to the specificed path\n   * @param path the path to be registered\n   * @param initialData optional data to send immediately to this client (and only this client).\n   *   This can also be a Promise, in which case the resolved data will be sent to the client. If\n   *   the client is unsubscribed before the Promise resolves, no data will be sent.\n   */\n  subscribeClient<T = unknown>(client: NydusClient, path: string, initialData?: T | Promise<T>) {\n    const newSubs = this.subscriptions.update(path, Set(), s => s.add(client))\n    if (newSubs === this.subscriptions) {\n      return // client was previously subscribed\n    }\n    this.subscriptions = newSubs\n    client.subscriptions = client.subscriptions.add(path)\n    if (initialData !== undefined) {\n      const castInitialData = initialData as any\n      if (!!castInitialData && typeof castInitialData.then === 'function') {\n        const promise = castInitialData as Promise<T>\n        promise.then((result: T) => {\n          if (this.subscriptions.get(path)?.has(client) && result !== undefined) {\n            client.send(PACKAGE_ONLY, encode(MessageType.Publish, result, undefined, path))\n          }\n        })\n      } else {\n        client.send(PACKAGE_ONLY, encode(MessageType.Publish, initialData, undefined, path))\n      }\n    }\n  }\n\n  /** Remove a client's subsription to a path (if it was subscribed). */\n  unsubscribeClient(client: NydusClient, path: string) {\n    // NOTE(tec27): The typings on Immutable are kind of wrong here, and returning undefined is\n    // valid for update\n    const newSubs = this.subscriptions.update(path, s => s?.delete(client) as any)\n    if (newSubs === this.subscriptions) {\n      return false // client wasn't subscribed before\n    }\n    this.subscriptions = newSubs\n    client.subscriptions = client.subscriptions.delete(path)\n    return true\n  }\n\n  /**\n   * Unsubscribe all the clients currently subscribed to a path.\n   */\n  unsubscribeAll(path: string) {\n    const subs = this.subscriptions.get(path)\n    if (!subs) {\n      return false\n    }\n\n    for (const c of subs.values()) {\n      c.subscriptions = c.subscriptions.delete(path)\n    }\n    this.subscriptions = this.subscriptions.delete(path)\n    return true\n  }\n\n  /**\n   * Publish a message to all clients subscribed to `path`.\n   */\n  publish<T = unknown>(path: string, data?: T) {\n    const subs = this.subscriptions.get(path)\n    if (!subs) return\n\n    const packet = encode(MessageType.Publish, data, undefined, path)\n    for (const c of subs.values()) {\n      c.send(PACKAGE_ONLY, packet)\n    }\n  }\n\n  private onConnection(socket: Socket) {\n    const client = new NydusClient(\n      this.idGen(),\n      socket,\n      this.onInvokeFunc,\n      this.onCloseFunc,\n      this.onParserErrorFunc,\n    )\n    this.clients = this.clients.set(client.id, client)\n    socket.send(encode(MessageType.Welcome, protocolVersion), undefined)\n    this.emit('connection', client)\n  }\n\n  private onDisconnect(client: NydusClient) {\n    const subs = client.subscriptions\n    for (const path of subs.values()) {\n      // NOTE(tec27): The typings on Immutable are kind of wrong here, and returning undefined is\n      // valid for update\n      this.subscriptions = this.subscriptions.update(path, s => s?.delete(client) as any)\n    }\n  }\n\n  private onInvoke(client: NydusClient, msg: NydusInvokeMessage<unknown>) {\n    const route = this.router.match(msg.path)\n    if (!route) {\n      client.send(PACKAGE_ONLY, encode(MessageType.Error, NOT_FOUND, msg.id))\n      return\n    }\n\n    const initData = Map({\n      server: this,\n      client,\n      path: route.route,\n      params: fromJS(route.params),\n      splats: fromJS(route.splats),\n      body: msg.data,\n    })\n\n    route\n      .action(initData)\n      .then(result => {\n        client.send(PACKAGE_ONLY, encode(MessageType.Result, result, msg.id))\n      })\n      .catch(err => {\n        let result\n        try {\n          result = this.invokeErrorConverter(err, client)\n          if (\n            result &&\n            typeof result === 'object' &&\n            result.hasOwnProperty('status') &&\n            (result as any).status === 500\n          ) {\n            this.emit('invokeError', err, client, msg)\n          }\n        } catch (convertErr: any) {\n          result = { status: 500, message: STATUS_CODES[500] }\n          this.emit('error', convertErr)\n        }\n        client.send(PACKAGE_ONLY, encode(MessageType.Error, result, msg.id))\n      })\n  }\n\n  private onParserError(client: NydusClient, msg: string) {\n    this.emit('parserError', client, msg)\n  }\n}\n\nexport default function createNydusServer(\n  httpServer: http.Server,\n  opts: Partial<NydusServerOptions> = {},\n) {\n  const nydus = new NydusServer(opts)\n  nydus.attach(httpServer, opts)\n  return nydus\n}\n"],"mappings":"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"}