@joystick.js/db-canary 0.0.0-canary.2224 → 0.0.0-canary.2225

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import h from"net";import p from"../lib/op_types.js";import{send_success as u,send_error as d,send_message as l}from"../lib/send_response.js";import{shutdown_write_queue as g}from"../lib/write_queue.js";import{create_message_parser as w,encode_message as c}from"../lib/tcp_protocol.js";import m from"../lib/logger.js";import{initialize_database as f,cleanup_database as y}from"../lib/query_engine.js";import{handle_admin_operation as b,handle_ping_operation as v}from"../lib/operation_dispatcher.js";import{get_settings as k}from"../lib/load_settings.js";class ${constructor(){this.server=null,this.connections=new Map,this.connection_count=0,this.settings=null,this.port=null,this.write_id_counter=0,this.pending_writes=new Map,this.authenticated_clients=new Set,this.heartbeat_interval=null;const{create_context_logger:e}=m("worker");this.log=e({worker_pid:process.pid}),this.setup_worker()}setup_worker(){process.on("message",e=>{this.handle_master_message(e)}),process.on("SIGTERM",()=>{this.shutdown()}),process.on("SIGINT",()=>{this.shutdown()}),this.send_heartbeat(),this.heartbeat_interval=setInterval(()=>{this.send_heartbeat()},5e3),process.connected&&process.send({type:"worker_ready"})}handle_master_message(e){switch(e.type){case"config":this.handle_config(e);break;case"write_response":this.handle_write_response(e);break;case"auth_response":this.handle_auth_response(e);break;case"setup_response":this.handle_setup_response(e);break;case"write_notification":this.handle_write_notification(e);break;case"shutdown":this.shutdown();break;default:this.log.warn("Unknown message type received from master",{message_type:e.type})}}handle_config(e){const t=e.data.master_id;if(this.master_id&&this.master_id!==t){this.log.info("Worker already configured by different master, ignoring config message",{current_master_id:this.master_id,incoming_master_id:t,current_port:this.port,new_port:e.data.port});return}if(this.port!==null&&this.master_id===t){this.log.info("Worker already configured by same master, ignoring duplicate config message",{master_id:t,current_port:this.port,new_port:e.data.port});return}this.log.info("Received config message",{port:e.data.port,master_id:t}),this.port=e.data.port,this.settings=e.data.settings,this.master_id=t;try{let s="./data";try{const r=k();r?.data_path&&(s=r.data_path)}catch{}f(s),this.log.info("Database initialized in worker process",{database_path:s})}catch(s){this.log.error("Failed to initialize database in worker process",{error:s.message})}this.log.info("Starting server",{port:this.port}),this.start_server()}start_server(){this.server=h.createServer(e=>{this.handle_connection(e)}),this.server.listen(this.port,()=>{this.log.info("Server listening",{port:this.port}),process.connected&&process.send({type:"server_ready"})}),this.server.on("error",e=>{this.log.error("Server error",{error:e.message})})}handle_connection(e){const t=`${process.pid}_${Date.now()}_${Math.random()}`;e.id=t,e.message_parser=w(),this.connections.set(t,e),this.connection_count++,this.update_connection_count(),e.on("data",s=>{this.handle_socket_data(e,s)}),e.on("end",()=>{this.handle_socket_end(e)}),e.on("error",s=>{this.log.error("Socket error",{socket_id:t,error_message:s.message}),this.handle_socket_end(e)})}handle_socket_data(e,t){try{const s=e.message_parser.parse_messages(t);for(const r of s){const a=r,i=a?.op||null;if(!i){d(e,{message:"Missing operation type"});continue}if(!this.check_op_type(i)){d(e,{message:"Invalid operation type"});continue}this.route_operation(e,i,a?.data||{})}}catch(s){this.log.error("Data parsing error",{socket_id:e.id,error_message:s.message}),d(e,{message:"Invalid data format"})}}handle_socket_end(e){e.id&&(this.connections.delete(e.id),this.authenticated_clients.delete(e.id),this.connection_count--,this.update_connection_count()),this.log.info("Client disconnected",{socket_id:e.id})}check_op_type(e=""){return e?p.includes(e):!1}route_operation(e,t,s){switch(t){case"authentication":this.handle_authentication(e,s);break;case"setup":this.handle_setup(e,s);break;case"find_one":case"find":case"get_indexes":this.handle_read_operation(e,t,s);break;case"create_index":case"drop_index":this.handle_write_operation(e,t,s);break;case"insert_one":case"update_one":case"delete_one":case"bulk_write":this.handle_write_operation(e,t,s);break;case"ping":v(e);break;case"admin":b(e,s,this.is_authenticated.bind(this));break;default:d(e,{message:`Unsupported operation: ${t}`})}}handle_authentication(e,t){if(this.is_authenticated(e))l(e,"Already authenticated");else{const s=`${e.id}_${Date.now()}`;process.send({type:"auth_request",data:{auth_id:s,socket_id:e.id,password:t.password}}),this.pending_writes.set(s,{socket:e,type:"auth"})}}handle_setup(e,t){const s=`${e.id}_${Date.now()}`;process.send({type:"setup_request",data:{setup_id:s,socket_id:e.id}}),this.pending_writes.set(s,{socket:e,type:"setup"})}handle_read_operation(e,t,s){if(!this.is_authenticated(e)){d(e,{message:"Authentication required"});return}const r=`${e.id}_${++this.write_id_counter}`;process.send({type:"write_request",data:{write_id:r,op_type:t,data:s,socket_id:e.id}}),this.pending_writes.set(r,{socket:e,type:"read"})}handle_write_operation(e,t,s){if(!this.is_authenticated(e)){d(e,{message:"Authentication required"});return}const r=`${e.id}_${++this.write_id_counter}`;process.send({type:"write_request",data:{write_id:r,op_type:t,data:s,socket_id:e.id}}),this.pending_writes.set(r,{socket:e,type:"write"})}handle_write_response(e){const{write_id:t,success:s,result:r,error:a}=e.data,i=this.pending_writes.get(t);if(!i){this.log.warn("No pending write found",{write_id:t});return}const{socket:o}=i;if(this.pending_writes.delete(t),o.destroyed||!o.writable){this.log.warn("Socket disconnected before response could be sent",{write_id:t});return}try{if(s){let n;Array.isArray(r)?n={ok:1,documents:r}:r&&typeof r=="object"?n={ok:1,...r}:n={ok:1,result:r};const _=c(n);o.write(_)}else{const _=c({ok:0,error:a});o.write(_)}}catch(n){this.log.error("Error sending response to client",{write_id:t,error:n.message})}}handle_auth_response(e){const{auth_id:t,success:s,message:r}=e.data,a=this.pending_writes.get(t);if(!a){this.log.warn("No pending auth found",{auth_id:t});return}const{socket:i}=a;if(this.pending_writes.delete(t),i.destroyed||!i.writable){this.log.warn("Socket disconnected before auth response could be sent",{auth_id:t});return}try{if(s){this.authenticated_clients.add(i.id);const n=c({ok:1,version:"1.0.0",message:r});i.write(n)}else d(i,{message:r}),i.end()}catch(o){this.log.error("Error sending auth response to client",{auth_id:t,error:o.message})}}handle_setup_response(e){const{setup_id:t,success:s,password:r,message:a,error:i}=e.data,o=this.pending_writes.get(t);if(!o){this.log.warn("No pending setup found",{setup_id:t});return}const{socket:n}=o;this.pending_writes.delete(t),s?u(n,{password:r,message:a}):d(n,{message:i})}handle_write_notification(e){this.log.info("Received write notification",{op_type:e.data.op_type,timestamp:e.data.timestamp})}is_authenticated(e){return this.authenticated_clients.has(e.id)}update_connection_count(){process.connected&&process.send({type:"connection_count",data:{count:this.connection_count}})}send_heartbeat(){if(process.connected)try{process.send({type:"heartbeat",data:{timestamp:Date.now()}})}catch{clearInterval(this.heartbeat_interval)}}async shutdown(){const e=Date.now();this.log.info("Initiating graceful shutdown");try{await g(),this.log.info("Write queue shutdown complete")}catch(s){this.log.error("Error shutting down write queue",{error:s.message})}try{await y(),this.log.info("Database cleanup complete")}catch(s){this.log.error("Error cleaning up database",{error:s.message})}this.server&&this.server.close(()=>{this.log.info("Server closed")});for(const[s,r]of this.connections)r.end();const t=process.env.NODE_ENV==="test"?100:5e3;setTimeout(()=>{const s=Date.now()-e;this.log.info("Worker shutdown complete",{shutdown_duration_ms:s}),process.exit(0)},t)}}const z=new $;
1
+ import h from"net";import p from"../lib/op_types.js";import{send_success as u,send_error as d,send_message as l}from"../lib/send_response.js";import{shutdown_write_queue as g}from"../lib/write_queue.js";import{create_message_parser as w,encode_message as c}from"../lib/tcp_protocol.js";import m from"../lib/logger.js";import{initialize_database as f,cleanup_database as y}from"../lib/query_engine.js";import{handle_admin_operation as b,handle_ping_operation as v}from"../lib/operation_dispatcher.js";import{get_settings as k}from"../lib/load_settings.js";import{is_development_mode as $}from"../lib/development_mode.js";class S{constructor(){this.server=null,this.connections=new Map,this.connection_count=0,this.settings=null,this.port=null,this.write_id_counter=0,this.pending_writes=new Map,this.authenticated_clients=new Set,this.heartbeat_interval=null;const{create_context_logger:e}=m("worker");this.log=e({worker_pid:process.pid}),this.setup_worker()}setup_worker(){process.on("message",e=>{this.handle_master_message(e)}),process.on("SIGTERM",()=>{this.shutdown()}),process.on("SIGINT",()=>{this.shutdown()}),this.send_heartbeat(),this.heartbeat_interval=setInterval(()=>{this.send_heartbeat()},5e3),process.connected&&process.send({type:"worker_ready"})}handle_master_message(e){switch(e.type){case"config":this.handle_config(e);break;case"write_response":this.handle_write_response(e);break;case"auth_response":this.handle_auth_response(e);break;case"setup_response":this.handle_setup_response(e);break;case"write_notification":this.handle_write_notification(e);break;case"shutdown":this.shutdown();break;default:this.log.warn("Unknown message type received from master",{message_type:e.type})}}handle_config(e){const t=e.data.master_id;if(this.master_id&&this.master_id!==t){this.log.info("Worker already configured by different master, ignoring config message",{current_master_id:this.master_id,incoming_master_id:t,current_port:this.port,new_port:e.data.port});return}if(this.port!==null&&this.master_id===t){this.log.info("Worker already configured by same master, ignoring duplicate config message",{master_id:t,current_port:this.port,new_port:e.data.port});return}this.log.info("Received config message",{port:e.data.port,master_id:t}),this.port=e.data.port,this.settings=e.data.settings,this.master_id=t;try{let s="./data";try{const r=k();r?.data_path&&(s=r.data_path)}catch{}f(s),this.log.info("Database initialized in worker process",{database_path:s})}catch(s){this.log.error("Failed to initialize database in worker process",{error:s.message})}this.log.info("Starting server",{port:this.port}),this.start_server()}start_server(){this.server=h.createServer(e=>{this.handle_connection(e)}),this.server.listen(this.port,()=>{this.log.info("Server listening",{port:this.port}),process.connected&&process.send({type:"server_ready"})}),this.server.on("error",e=>{this.log.error("Server error",{error:e.message})})}handle_connection(e){const t=`${process.pid}_${Date.now()}_${Math.random()}`;e.id=t,e.message_parser=w(),this.connections.set(t,e),this.connection_count++,this.update_connection_count(),e.on("data",s=>{this.handle_socket_data(e,s)}),e.on("end",()=>{this.handle_socket_end(e)}),e.on("error",s=>{this.log.error("Socket error",{socket_id:t,error_message:s.message}),this.handle_socket_end(e)})}handle_socket_data(e,t){try{const s=e.message_parser.parse_messages(t);for(const r of s){const a=r,i=a?.op||null;if(!i){d(e,{message:"Missing operation type"});continue}if(!this.check_op_type(i)){d(e,{message:"Invalid operation type"});continue}this.route_operation(e,i,a?.data||{})}}catch(s){this.log.error("Data parsing error",{socket_id:e.id,error_message:s.message}),d(e,{message:"Invalid data format"})}}handle_socket_end(e){e.id&&(this.connections.delete(e.id),this.authenticated_clients.delete(e.id),this.connection_count--,this.update_connection_count()),this.log.info("Client disconnected",{socket_id:e.id})}check_op_type(e=""){return e?p.includes(e):!1}route_operation(e,t,s){switch(t){case"authentication":this.handle_authentication(e,s);break;case"setup":this.handle_setup(e,s);break;case"find_one":case"find":case"get_indexes":this.handle_read_operation(e,t,s);break;case"create_index":case"drop_index":this.handle_write_operation(e,t,s);break;case"insert_one":case"update_one":case"delete_one":case"bulk_write":this.handle_write_operation(e,t,s);break;case"ping":v(e);break;case"admin":b(e,s,this.is_authenticated.bind(this));break;default:d(e,{message:`Unsupported operation: ${t}`})}}handle_authentication(e,t){if(this.is_authenticated(e))l(e,"Already authenticated");else{const s=`${e.id}_${Date.now()}`;process.send({type:"auth_request",data:{auth_id:s,socket_id:e.id,password:t.password}}),this.pending_writes.set(s,{socket:e,type:"auth"})}}handle_setup(e,t){const s=`${e.id}_${Date.now()}`;process.send({type:"setup_request",data:{setup_id:s,socket_id:e.id}}),this.pending_writes.set(s,{socket:e,type:"setup"})}handle_read_operation(e,t,s){if(!this.is_authenticated(e)){d(e,{message:"Authentication required"});return}const r=`${e.id}_${++this.write_id_counter}`;process.send({type:"write_request",data:{write_id:r,op_type:t,data:s,socket_id:e.id}}),this.pending_writes.set(r,{socket:e,type:"read"})}handle_write_operation(e,t,s){if(!this.is_authenticated(e)){d(e,{message:"Authentication required"});return}const r=`${e.id}_${++this.write_id_counter}`;process.send({type:"write_request",data:{write_id:r,op_type:t,data:s,socket_id:e.id}}),this.pending_writes.set(r,{socket:e,type:"write"})}handle_write_response(e){const{write_id:t,success:s,result:r,error:a}=e.data,i=this.pending_writes.get(t);if(!i){this.log.warn("No pending write found",{write_id:t});return}const{socket:o}=i;if(this.pending_writes.delete(t),o.destroyed||!o.writable){this.log.warn("Socket disconnected before response could be sent",{write_id:t});return}try{if(s){let n;Array.isArray(r)?n={ok:1,documents:r}:r&&typeof r=="object"?n={ok:1,...r}:n={ok:1,result:r};const _=c(n);o.write(_)}else{const _=c({ok:0,error:a});o.write(_)}}catch(n){this.log.error("Error sending response to client",{write_id:t,error:n.message})}}handle_auth_response(e){const{auth_id:t,success:s,message:r}=e.data,a=this.pending_writes.get(t);if(!a){this.log.warn("No pending auth found",{auth_id:t});return}const{socket:i}=a;if(this.pending_writes.delete(t),i.destroyed||!i.writable){this.log.warn("Socket disconnected before auth response could be sent",{auth_id:t});return}try{if(s){this.authenticated_clients.add(i.id);const n=c({ok:1,version:"1.0.0",message:r});i.write(n)}else d(i,{message:r}),i.end()}catch(o){this.log.error("Error sending auth response to client",{auth_id:t,error:o.message})}}handle_setup_response(e){const{setup_id:t,success:s,password:r,message:a,error:i}=e.data,o=this.pending_writes.get(t);if(!o){this.log.warn("No pending setup found",{setup_id:t});return}const{socket:n}=o;this.pending_writes.delete(t),s?u(n,{password:r,message:a}):d(n,{message:i})}handle_write_notification(e){this.log.info("Received write notification",{op_type:e.data.op_type,timestamp:e.data.timestamp})}is_authenticated(e){return $()?!0:this.authenticated_clients.has(e.id)}update_connection_count(){process.connected&&process.send({type:"connection_count",data:{count:this.connection_count}})}send_heartbeat(){if(process.connected)try{process.send({type:"heartbeat",data:{timestamp:Date.now()}})}catch{clearInterval(this.heartbeat_interval)}}async shutdown(){const e=Date.now();this.log.info("Initiating graceful shutdown");try{await g(),this.log.info("Write queue shutdown complete")}catch(s){this.log.error("Error shutting down write queue",{error:s.message})}try{await y(),this.log.info("Database cleanup complete")}catch(s){this.log.error("Error cleaning up database",{error:s.message})}this.server&&this.server.close(()=>{this.log.info("Server closed")});for(const[s,r]of this.connections)r.end();const t=process.env.NODE_ENV==="test"?100:5e3;setTimeout(()=>{const s=Date.now()-e;this.log.info("Worker shutdown complete",{shutdown_duration_ms:s}),process.exit(0)},t)}}const T=new S;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@joystick.js/db-canary",
3
3
  "type": "module",
4
- "version": "0.0.0-canary.2224",
5
- "canary_version": "0.0.0-canary.2223",
4
+ "version": "0.0.0-canary.2225",
5
+ "canary_version": "0.0.0-canary.2224",
6
6
  "description": "JoystickDB - A minimalist database server for the Joystick framework",
7
7
  "main": "./dist/server/index.js",
8
8
  "scripts": {
@@ -13,6 +13,7 @@ import create_logger from '../lib/logger.js';
13
13
  import { initialize_database, cleanup_database } from '../lib/query_engine.js';
14
14
  import { handle_admin_operation, handle_ping_operation } from '../lib/operation_dispatcher.js';
15
15
  import { get_settings } from '../lib/load_settings.js';
16
+ import { is_development_mode } from '../lib/development_mode.js';
16
17
 
17
18
  /**
18
19
  * Cluster worker process that handles TCP connections and routes operations to master.
@@ -481,6 +482,11 @@ class ClusterWorker {
481
482
  }
482
483
 
483
484
  is_authenticated(socket) {
485
+ // NOTE: Bypass authentication in development mode for TCP connections.
486
+ if (is_development_mode()) {
487
+ return true;
488
+ }
489
+
484
490
  return this.authenticated_clients.has(socket.id);
485
491
  }
486
492