<commit_before>###
Bongo.js
Unfancy models for MongoDB

(c) 2011 Koding, Inc.

@class: bongo
@description: the main klass of the library.
@author: <NAME> <<EMAIL>>
###

{EventEmitter} = require 'events'

module.exports = class Bongo extends EventEmitter
  [READY, NOTREADY] = [0,1]
  # core
  url = require 'url'
  fs = require 'fs'
  nodePath = require 'path'
  # bongo = new EventEmitter
  # client-side boilerplate
  @client = bongoClient = require 'bongo-client'
  # dnode           = require 'koding-dnode'
  # contrib
  @mongo          = require 'mongoskin'
  {extend}        = require 'underscore'
  @JsPath = JsPath = require 'jspath'
  @Inflector      = require 'inflector'
  # lib
  # the base klass of the library:
  @Base = Base    = require './base'
  # the model klass of the library:
  @Model = Model  = require './model'
  ModelRegistry   = require './modelregistry'
  # the rest of the basics:
  @Subcollection  = require './subcollection'
  @ObjectId       = require './objectid'
  @ObjectRef      = require './objectref'
  @Validator      = require './validator'
  @Plugin         = require './plugin'
  @Register       = require './register'
  # @KiteServer     = require './kiteserver'
  @util           = require './util'

  {@daisy, @dash, @sequence, @race, @future} = require 'sinkrow'

  {Scrubber, Store} = require 'koding-dnode-protocol'

  @secure =(fn)->
    fn.isSecurityEnabled = yes
    fn
  
  {@asynchronizeOwnMethods} = require './util'
  
  {BongoError}    = require './errortypes'

  Client = require './client'
  
  @throwIt =(it)-> throw it if it?

  constructor:(options)->
    {@mq, @mongo, @queueName, @root, @fetchClient, models} = options
    @setClient @mongo
    @localStore = new Store
    @remoteStore = new Store
    @clients = {}
    @models = {}
    @loadModels models
    @on 'error', Bongo.throwIt
    EventEmitter.call(@)
    @setMaxListeners 0

  defineModel:(name, model)->
    @models[name] = model
    model.addBongo @
    @emit 'newModel', name, model
    model.on 'needsPopulated', (def, path)=>
      path = if Array.isArray path then path else path.split '.'
      names = JsPath.getAt def, path
      unless Array.isArray names
        names = [names]
        JsPath.setAt def, path, names
      names.forEach (name, i)=>
        if target = @models[name]
          JsPath.setAt def, path.concat(i), target
      @on 'newModel', (newName, newModel)->
        if newName in names
          JsPath.setAt def, path.concat(names.indexOf newName), newModel

  listModels=(root, modelPath, acc={})->
    path = nodePath.join(root, modelPath)
    stat = fs.statSync path
    if stat.isDirectory()
      names = fs.readdirSync path
      for name in names
        listModels root, nodePath.join(modelPath, name), acc
    else if /(\.coffee|\.js)$/.test path
      acc[path] = NOTREADY
    return acc

  createModelRegistry:(modelPath)->
    acc = {}
    if Array.isArray(modelPath)
      listModels(@root, item, acc) for item in modelPath
    else
      listModels(@root, modelPath, acc)
    new ModelRegistry acc

  loadModels:(modelPath)->
    @registry = @createModelRegistry(modelPath)
    @registry.forEach (path)=>
      try model = require path
      catch e then return @emit 'error', e
      if model.name? and ('function' is typeof model)
        @defineModel model.name, model
      else for own name, konstructor of model
        @defineModel name, konstructor
      @registry.setState path, READY
    @emit 'ready'

  handleEvent:(type, ctx, event, rest)->
    switch type
      when 'instance'
        if event is 'updateInstance'
          [event, data] = rest
          @mq.emit 'updateInstances', event, data, autoDelete: false
        else if event is 'global-listener-added'
          [actualEvent, listener] = rest
          @mq.on 'event-'+ctx.getId(), actualEvent, listener
        else
          @mq.emit 'event-'+ctx.getId(), event, rest
      when 'static'
        if event is 'global-listener-added'
          [actualEvent, listener] = rest
          @mq.on 'event-'+ctx.name, actualEvent, listener
        else
          [data] = rest
          @mq.emit 'event-'+ctx.name, event, data

  connect:(callback)->
    @mq.ready =>
      @mq.connection.queue @queueName, {autoDelete:no}, (queue)=>
        queue.subscribe (message, headers, deliveryInfo)=>
          {exchange, routingKey} = deliveryInfo
          msg = ''+message?.data
          switch routingKey
            when 'client-message'
              @handleRequest exchange, msg
            when 'client-join'
              message =
                method    : 'defineApi'
                arguments : [@describeApi()]
                callbacks : {}
              @mq.emit exchange, 'server-message', message
              @handleAuth exchange, msg

  handleAuth:(secretName, sessionToken)->
    @emit 'auth', secretName, sessionToken

  handleRequest:(secretName, message)->
    message = JSON.parse(message)
    {method, sessionToken} = message
    scrubber = new Scrubber @localStore
    unscrubbed = scrubber.unscrub message, (callbackId)=>
      unless @remoteStore.has(secretName+callbackId)
          @remoteStore.add secretName+callbackId, (args...)=>
            @handleResponse secretName, callbackId, args
        @remoteStore.get secretName+callbackId
    if method is 'fetchApi'
      @fetchApi.apply @, unscrubbed
    else
      @invokeMethod method, unscrubbed, secretName, sessionToken

  invokeMethod:do->
    ###
    @helper apply()
    @private
    @description - apply the method, conditionally currying the "client" object
    ###
    apply =(bongo, ctx, method, args, sessionToken)->
      return unless ctx?
      fn = ctx[method]
      if fn.isSecurityEnabled
        bongo.fetchClient sessionToken, (client)->
          args = [client].concat args
          fn.apply ctx, args
      else
        fn.apply ctx, args
    ###
    @implementation
    ###
    (method, args, secretName, sessionToken)->
      unless method?
        @handleError 'No such method'
      else if method?.method
        konstructor = Base.constructors[method.constructorName]
        switch method.type
          when 'static'
            if konstructor.hasSharedMethod method.method
              apply @, konstructor, method.method, args, sessionToken
            else
              @handleError "No such method! #{JSON.stringify method} #{args} #{secretName}"
          when 'instance'
            if konstructor::hasSharedMethod method.method
              if method.id
                konstructor.one? _id: method.id, (err, instance)=>
                  if err
                    @handleError err
                  else
                    apply @, instance, method.method, args, sessionToken
              else if method.data
                instance = new konstructor data
                apply @, instance, method.method, args, sessionToken
            else
              @handleError "No such method! #{JSON.stringify method} #{args} #{secretName}"
          else
            @handleError 'Unknown method type!'
      else
        @handleError 'Unknown method type!'

  handleError:(message)->
    @emit 'error',
      if message?.message then {message: message.message}
      else {message}

  handleResponse:(secretName, callbackId, args)->
    scrubber = new Scrubber @localStore
    scrubber.scrub args, =>
      message = scrubber.toDnodeProtocol()
      message.method = callbackId
      @mq.emit secretName, 'server-message', JSON.stringify(message)

  disconnect:(callback)->
    # implement disconnect
  getMethodDescription:(name, konstructor)->
    description = konstructor.getSharedMethods()
    description.options = {
      broadcastable: konstructor.getBroadcastable?()
    }
    description

  describeApi:(callback)->
    api = {}
    # share the global constructors
    for own name, konstructor of Base.globalSharedConstructors
      api[name] = @getMethodDescription name, konstructor
    # share the constructors which are specific to this bongo instance
    for own name, konstructor of @models when konstructor.isShared
      api[name] = @getMethodDescription name, konstructor
    api

  dispatchMethod:(contructorName, method, context, args)->
    # implement dispatch method

  setClient:(rest...)->
    # throw new Error 'unimplemented'
    Model.setClient rest...
  
  pre:-> console.log 'unimplemented'

  fetchApi:(callback)->
    console.log 'returning the api'
    callback null, @describeApi()

  # middlewares_ = {}
  # @applyMiddleware =(event, rest...)->
  #   listeners = middlewares_[event]
  #   if listeners?
  #     listener rest... for listener in listeners
  #   else
  #     [rest..., callback] = rest
  #     callback? rest...
  
  # @pre =(event, listener)->
  #   middlewares_[event] ?= []
  #   middlewares_[event].push listener
  
  # clientCode_ = null
  # # do ->
  # #   # this is Koding's special implementation of dnode-protocol
  # #   {Scrubber} = require 'koding-dnode-protocol'
  # #   Scrubber.use (cursor)->
  # #     {node} = cursor
  # #     if 'function' is typeof node?.toObjectRef and not node.payload_
  # #       cursor.update node.toObjectRef()

  # @Client = Client = require './client'

  # getDelegatedPrototypeInterface_ =(constructor, methodName, client)->
  #   invokeMethod =(instance, args, client)->
  #     if client
  #       constructor::[methodName].apply instance, [client].concat args
  #     else
  #       constructor::[methodName].apply instance, args
  #   (channelId, _id, data, args...)->
  #     refreshClient client, (client)->
  #       if _id?
  #         constructor.one {_id}, (err, instance)->
  #           if err
  #             console.log 'There was an error', err
  #           else if instance?
  #             invokeMethod instance, args, client
  #           else
  #             console.log 'unknown instance', constructor.name, _id
  #       else
  #         # instance = constructor.sharedInstances[data.bongo_?.instanceId] = new constructor data
  #         instance = new constructor data
  #         invokeMethod instance, args, client

  # getClient =(conn)-> new Client conn
  
  # refreshClient =(client, callback)->
  #   bongo.applyMiddleware 'methodIsInvoked', client, callback
  
  # api =(remote, conn)->
  #   conn.on 'end', ->
  #     Kite.removeById conn.remoteId
  #     bongo.emit 'clientDidDisconnect', conn
    
  #   fetchInstance:(constructorName, _id, callback)->
  #     konstructor = Base.constructors[constructorName]
  #     if konstructor?.isShared
  #       konstructor?.one {_id}, (err, node)->
  #         if err or !node? then callback err
  #         else
  #           node.payload_ = yes
  #           callback null, node
  #     else callback null
    
  #   fetchApi:(callback)->
  #     api = for own name, constructor of Base.sharedConstructors
  #       {static, instance} = constructor.getSharedMethods()
  #       staticMethods = {}
  #       instanceMethods = {}
  #       rawStaticMethods = []
  #       rawInstanceMethods = []
  #       client = getClient conn
  #       for methodName in static
  #         do (methodName, constructor)->
  #           try
  #             method = constructor[methodName]
  #             rawStaticMethods.push methodName if method.useMQ
  #             boundMethod =
  #               if method.isSecurityEnabled
  #                 (channelId, rest...)->
  #                   refreshClient client, (client)->
  #                     method.apply constructor, [client].concat rest
  #               else
  #                 (channelId, rest...)->
  #                   method.apply constructor, rest
  #             staticMethods[methodName] = boundMethod
  #           catch e
  #             throw new BongoError \
  #               """
  #               Shared method #{methodName} could not be bound.
  #               """
  #       for methodName in instance
  #         try
  #           if constructor::[methodName].useMQ
  #             rawInstanceMethods.push methodName
  #           instanceMethods[methodName] =
  #             getDelegatedPrototypeInterface_ constructor, methodName,
  #               (client if constructor::[methodName].isSecurityEnabled)
  #         catch e
  #           throw new BongoError \
  #             """
  #             Shared method #{methodName} could not be bound.
  #             """
  #       {
  #         name
  #         staticMethods, instanceMethods
  #         rawStaticMethods, rawInstanceMethods
  #         options: {
  #           broadcastable: constructor.getBroadcastable()
  #         }
  #       }
  #     callback api
  
  # @useMQ =(fn)->
  #   fn.useMQ = yes
  #   fn
  
  # @setClient =(overload)->
  #   Model.setClient overload
  #   @

  # @listen =(server, options={})->
  #   {client} = options
  #   client or= '/bongo.js'
  #   delete options.client
  #   dnode(api).listen server, options, (remote, conn)->
  #     remote.fetchClientId? (id)-> conn.remoteId = id
  #   if 'function' is typeof server.listeners
  #     listeners = server.listeners 'request'
  #     server.removeAllListeners 'request'
  #     server.on 'request', (req, res)->
  #       {pathname} = url.parse req.url
  #       if pathname is client
  #         if clientCode_
  #           res.writeHead 200, 'Content-Type': 'text/javascript'
  #           res.end clientCode_
  #         else
  #           fs.readFile "#{bongoClient.browserPath}#{client}", (err, source)->
  #             if err
  #               throw err
  #               res.writeHead 500
  #               res.end "500 - browser code could not be loaded."
  #             else
  #               res.writeHead 200, 'Content-Type': 'text/javascript'
  #               res.end clientCode_ = source
  #       else
  #         listener.call @, req, res for listener in listeners
  #   @
  
  # @fetchChannel =(name, callback)->
  #   Channel = require('./channel') bongo.mq
  #   callback new Channel(name)
  #   <commit_msg>Remove auto-delete for updateInstances exchange<commit_after>###
Bongo.js
Unfancy models for MongoDB

(c) 2011 Koding, Inc.

@class: bongo
@description: the main klass of the library.
@author: <NAME> <<EMAIL>>
###

{EventEmitter} = require 'events'

module.exports = class Bongo extends EventEmitter
  [READY, NOTREADY] = [0,1]
  # core
  url = require 'url'
  fs = require 'fs'
  nodePath = require 'path'
  # bongo = new EventEmitter
  # client-side boilerplate
  @client = bongoClient = require 'bongo-client'
  # dnode           = require 'koding-dnode'
  # contrib
  @mongo          = require 'mongoskin'
  {extend}        = require 'underscore'
  @JsPath = JsPath = require 'jspath'
  @Inflector      = require 'inflector'
  # lib
  # the base klass of the library:
  @Base = Base    = require './base'
  # the model klass of the library:
  @Model = Model  = require './model'
  ModelRegistry   = require './modelregistry'
  # the rest of the basics:
  @Subcollection  = require './subcollection'
  @ObjectId       = require './objectid'
  @ObjectRef      = require './objectref'
  @Validator      = require './validator'
  @Plugin         = require './plugin'
  @Register       = require './register'
  # @KiteServer     = require './kiteserver'
  @util           = require './util'

  {@daisy, @dash, @sequence, @race, @future} = require 'sinkrow'

  {Scrubber, Store} = require 'koding-dnode-protocol'

  @secure =(fn)->
    fn.isSecurityEnabled = yes
    fn
  
  {@asynchronizeOwnMethods} = require './util'
  
  {BongoError}    = require './errortypes'

  Client = require './client'
  
  @throwIt =(it)-> throw it if it?

  constructor:(options)->
    {@mq, @mongo, @queueName, @root, @fetchClient, models} = options
    @setClient @mongo
    @localStore = new Store
    @remoteStore = new Store
    @clients = {}
    @models = {}
    @loadModels models
    @on 'error', Bongo.throwIt
    EventEmitter.call(@)
    @setMaxListeners 0

  defineModel:(name, model)->
    @models[name] = model
    model.addBongo @
    @emit 'newModel', name, model
    model.on 'needsPopulated', (def, path)=>
      path = if Array.isArray path then path else path.split '.'
      names = JsPath.getAt def, path
      unless Array.isArray names
        names = [names]
        JsPath.setAt def, path, names
      names.forEach (name, i)=>
        if target = @models[name]
          JsPath.setAt def, path.concat(i), target
      @on 'newModel', (newName, newModel)->
        if newName in names
          JsPath.setAt def, path.concat(names.indexOf newName), newModel

  listModels=(root, modelPath, acc={})->
    path = nodePath.join(root, modelPath)
    stat = fs.statSync path
    if stat.isDirectory()
      names = fs.readdirSync path
      for name in names
        listModels root, nodePath.join(modelPath, name), acc
    else if /(\.coffee|\.js)$/.test path
      acc[path] = NOTREADY
    return acc

  createModelRegistry:(modelPath)->
    acc = {}
    if Array.isArray(modelPath)
      listModels(@root, item, acc) for item in modelPath
    else
      listModels(@root, modelPath, acc)
    new ModelRegistry acc

  loadModels:(modelPath)->
    @registry = @createModelRegistry(modelPath)
    @registry.forEach (path)=>
      try model = require path
      catch e then return @emit 'error', e
      if model.name? and ('function' is typeof model)
        @defineModel model.name, model
      else for own name, konstructor of model
        @defineModel name, konstructor
      @registry.setState path, READY
    @emit 'ready'

  handleEvent:(type, ctx, event, rest)->
    switch type
      when 'instance'
        if event is 'updateInstance'
          [event, data] = rest
          @mq.emit 'updateInstances', event, data
        else if event is 'global-listener-added'
          [actualEvent, listener] = rest
          @mq.on 'event-'+ctx.getId(), actualEvent, listener
        else
          @mq.emit 'event-'+ctx.getId(), event, rest
      when 'static'
        if event is 'global-listener-added'
          [actualEvent, listener] = rest
          @mq.on 'event-'+ctx.name, actualEvent, listener
        else
          [data] = rest
          @mq.emit 'event-'+ctx.name, event, data

  connect:(callback)->
    @mq.ready =>
      @mq.connection.queue @queueName, {autoDelete:no}, (queue)=>
        queue.subscribe (message, headers, deliveryInfo)=>
          {exchange, routingKey} = deliveryInfo
          msg = ''+message?.data
          switch routingKey
            when 'client-message'
              @handleRequest exchange, msg
            when 'client-join'
              message =
                method    : 'defineApi'
                arguments : [@describeApi()]
                callbacks : {}
              @mq.emit exchange, 'server-message', message
              @handleAuth exchange, msg

  handleAuth:(secretName, sessionToken)->
    @emit 'auth', secretName, sessionToken

  handleRequest:(secretName, message)->
    message = JSON.parse(message)
    {method, sessionToken} = message
    scrubber = new Scrubber @localStore
    unscrubbed = scrubber.unscrub message, (callbackId)=>
      unless @remoteStore.has(secretName+callbackId)
          @remoteStore.add secretName+callbackId, (args...)=>
            @handleResponse secretName, callbackId, args
        @remoteStore.get secretName+callbackId
    if method is 'fetchApi'
      @fetchApi.apply @, unscrubbed
    else
      @invokeMethod method, unscrubbed, secretName, sessionToken

  invokeMethod:do->
    ###
    @helper apply()
    @private
    @description - apply the method, conditionally currying the "client" object
    ###
    apply =(bongo, ctx, method, args, sessionToken)->
      return unless ctx?
      fn = ctx[method]
      if fn.isSecurityEnabled
        bongo.fetchClient sessionToken, (client)->
          args = [client].concat args
          fn.apply ctx, args
      else
        fn.apply ctx, args
    ###
    @implementation
    ###
    (method, args, secretName, sessionToken)->
      unless method?
        @handleError 'No such method'
      else if method?.method
        konstructor = Base.constructors[method.constructorName]
        switch method.type
          when 'static'
            if konstructor.hasSharedMethod method.method
              apply @, konstructor, method.method, args, sessionToken
            else
              @handleError "No such method! #{JSON.stringify method} #{args} #{secretName}"
          when 'instance'
            if konstructor::hasSharedMethod method.method
              if method.id
                konstructor.one? _id: method.id, (err, instance)=>
                  if err
                    @handleError err
                  else
                    apply @, instance, method.method, args, sessionToken
              else if method.data
                instance = new konstructor data
                apply @, instance, method.method, args, sessionToken
            else
              @handleError "No such method! #{JSON.stringify method} #{args} #{secretName}"
          else
            @handleError 'Unknown method type!'
      else
        @handleError 'Unknown method type!'

  handleError:(message)->
    @emit 'error',
      if message?.message then {message: message.message}
      else {message}

  handleResponse:(secretName, callbackId, args)->
    scrubber = new Scrubber @localStore
    scrubber.scrub args, =>
      message = scrubber.toDnodeProtocol()
      message.method = callbackId
      @mq.emit secretName, 'server-message', JSON.stringify(message)

  disconnect:(callback)->
    # implement disconnect
  getMethodDescription:(name, konstructor)->
    description = konstructor.getSharedMethods()
    description.options = {
      broadcastable: konstructor.getBroadcastable?()
    }
    description

  describeApi:(callback)->
    api = {}
    # share the global constructors
    for own name, konstructor of Base.globalSharedConstructors
      api[name] = @getMethodDescription name, konstructor
    # share the constructors which are specific to this bongo instance
    for own name, konstructor of @models when konstructor.isShared
      api[name] = @getMethodDescription name, konstructor
    api

  dispatchMethod:(contructorName, method, context, args)->
    # implement dispatch method

  setClient:(rest...)->
    # throw new Error 'unimplemented'
    Model.setClient rest...
  
  pre:-> console.log 'unimplemented'

  fetchApi:(callback)->
    console.log 'returning the api'
    callback null, @describeApi()

  # middlewares_ = {}
  # @applyMiddleware =(event, rest...)->
  #   listeners = middlewares_[event]
  #   if listeners?
  #     listener rest... for listener in listeners
  #   else
  #     [rest..., callback] = rest
  #     callback? rest...
  
  # @pre =(event, listener)->
  #   middlewares_[event] ?= []
  #   middlewares_[event].push listener
  
  # clientCode_ = null
  # # do ->
  # #   # this is Koding's special implementation of dnode-protocol
  # #   {Scrubber} = require 'koding-dnode-protocol'
  # #   Scrubber.use (cursor)->
  # #     {node} = cursor
  # #     if 'function' is typeof node?.toObjectRef and not node.payload_
  # #       cursor.update node.toObjectRef()

  # @Client = Client = require './client'

  # getDelegatedPrototypeInterface_ =(constructor, methodName, client)->
  #   invokeMethod =(instance, args, client)->
  #     if client
  #       constructor::[methodName].apply instance, [client].concat args
  #     else
  #       constructor::[methodName].apply instance, args
  #   (channelId, _id, data, args...)->
  #     refreshClient client, (client)->
  #       if _id?
  #         constructor.one {_id}, (err, instance)->
  #           if err
  #             console.log 'There was an error', err
  #           else if instance?
  #             invokeMethod instance, args, client
  #           else
  #             console.log 'unknown instance', constructor.name, _id
  #       else
  #         # instance = constructor.sharedInstances[data.bongo_?.instanceId] = new constructor data
  #         instance = new constructor data
  #         invokeMethod instance, args, client

  # getClient =(conn)-> new Client conn
  
  # refreshClient =(client, callback)->
  #   bongo.applyMiddleware 'methodIsInvoked', client, callback
  
  # api =(remote, conn)->
  #   conn.on 'end', ->
  #     Kite.removeById conn.remoteId
  #     bongo.emit 'clientDidDisconnect', conn
    
  #   fetchInstance:(constructorName, _id, callback)->
  #     konstructor = Base.constructors[constructorName]
  #     if konstructor?.isShared
  #       konstructor?.one {_id}, (err, node)->
  #         if err or !node? then callback err
  #         else
  #           node.payload_ = yes
  #           callback null, node
  #     else callback null
    
  #   fetchApi:(callback)->
  #     api = for own name, constructor of Base.sharedConstructors
  #       {static, instance} = constructor.getSharedMethods()
  #       staticMethods = {}
  #       instanceMethods = {}
  #       rawStaticMethods = []
  #       rawInstanceMethods = []
  #       client = getClient conn
  #       for methodName in static
  #         do (methodName, constructor)->
  #           try
  #             method = constructor[methodName]
  #             rawStaticMethods.push methodName if method.useMQ
  #             boundMethod =
  #               if method.isSecurityEnabled
  #                 (channelId, rest...)->
  #                   refreshClient client, (client)->
  #                     method.apply constructor, [client].concat rest
  #               else
  #                 (channelId, rest...)->
  #                   method.apply constructor, rest
  #             staticMethods[methodName] = boundMethod
  #           catch e
  #             throw new BongoError \
  #               """
  #               Shared method #{methodName} could not be bound.
  #               """
  #       for methodName in instance
  #         try
  #           if constructor::[methodName].useMQ
  #             rawInstanceMethods.push methodName
  #           instanceMethods[methodName] =
  #             getDelegatedPrototypeInterface_ constructor, methodName,
  #               (client if constructor::[methodName].isSecurityEnabled)
  #         catch e
  #           throw new BongoError \
  #             """
  #             Shared method #{methodName} could not be bound.
  #             """
  #       {
  #         name
  #         staticMethods, instanceMethods
  #         rawStaticMethods, rawInstanceMethods
  #         options: {
  #           broadcastable: constructor.getBroadcastable()
  #         }
  #       }
  #     callback api
  
  # @useMQ =(fn)->
  #   fn.useMQ = yes
  #   fn
  
  # @setClient =(overload)->
  #   Model.setClient overload
  #   @

  # @listen =(server, options={})->
  #   {client} = options
  #   client or= '/bongo.js'
  #   delete options.client
  #   dnode(api).listen server, options, (remote, conn)->
  #     remote.fetchClientId? (id)-> conn.remoteId = id
  #   if 'function' is typeof server.listeners
  #     listeners = server.listeners 'request'
  #     server.removeAllListeners 'request'
  #     server.on 'request', (req, res)->
  #       {pathname} = url.parse req.url
  #       if pathname is client
  #         if clientCode_
  #           res.writeHead 200, 'Content-Type': 'text/javascript'
  #           res.end clientCode_
  #         else
  #           fs.readFile "#{bongoClient.browserPath}#{client}", (err, source)->
  #             if err
  #               throw err
  #               res.writeHead 500
  #               res.end "500 - browser code could not be loaded."
  #             else
  #               res.writeHead 200, 'Content-Type': 'text/javascript'
  #               res.end clientCode_ = source
  #       else
  #         listener.call @, req, res for listener in listeners
  #   @
  
  # @fetchChannel =(name, callback)->
  #   Channel = require('./channel') bongo.mq
  #   callback new Channel(name)
  #   
{'fraction_non_alphanumeric': 0.11298687363639615, 'fraction_numerical': 0.0013591330162022962, 'mean_word_length': 2.162180502148835, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 135, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '652344', 'n_tokens_mistral': 8078, 'n_tokens_neox': 7941, 'n_words': 2144}