├── angular-to-coldfusion ├── src │ ├── assets │ │ ├── .gitkeep │ │ └── cfwebsocket.js │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── styles.css │ ├── favicon.ico │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ ├── tslint.json │ ├── index.html │ ├── main.ts │ ├── browserslist │ ├── app │ │ ├── app.module.ts │ │ ├── app.component.html │ │ ├── app.component.css │ │ ├── app.component.spec.ts │ │ └── app.component.ts │ ├── test.ts │ ├── karma.conf.js │ └── polyfills.ts ├── e2e │ ├── src │ │ ├── app.po.ts │ │ └── app.e2e-spec.ts │ ├── tsconfig.e2e.json │ └── protractor.conf.js ├── .editorconfig ├── tsconfig.json ├── .gitignore ├── README.md ├── package.json ├── tslint.json └── angular.json ├── .gitignore ├── html-to-coldfusion ├── style.css ├── index.html ├── readme.md ├── script.js └── cfwebsocket.js ├── .github └── FUNDING.yml ├── chat ├── subscribers.cfm ├── ping.cfm ├── Application.cfc ├── broadcast.cfm ├── assets │ ├── chat.css │ └── chat.js ├── index.cfm └── ChatListener.cfc ├── favicon.ico ├── favicon.png ├── other-demo-files ├── socket.io_site │ ├── favicon.ico │ ├── .htaccess │ ├── server.json │ ├── Application.cfc │ ├── info.cfm │ ├── index.html │ ├── web.config │ ├── broadcast.cfm │ ├── styles.css │ ├── form.cfm │ └── socketio.client.js └── socket.io │ ├── public │ ├── favicon.ico │ ├── assets │ │ ├── app.css │ │ └── app.js │ └── index.html │ ├── package.json │ ├── server.js │ └── package-lock.json ├── .htaccess ├── instructions.md ├── live-demos ├── demo-00 │ ├── RestrictedListener.cfc │ ├── index.cfm │ ├── advanced.cfm │ └── restricted.cfm ├── ping.cfm ├── subscribers.cfm ├── assets │ ├── css │ │ ├── advanced.css │ │ └── styles.css │ └── js │ │ ├── advanced.js │ │ └── script.js ├── services │ └── communicate.cfc ├── demo-01 │ ├── index.cfm │ └── advanced.cfm ├── Application.cfc ├── html │ ├── index.html │ └── cfwebsocket.js └── broadcast.cfm ├── server.11.json ├── server.2021.json ├── server.2016.json ├── server.2018.json ├── server.2023.json ├── .editorconfig ├── shared └── fetch-joke.cfm ├── web.config ├── Application.cfc ├── LICENSE ├── swift-to-coldfusion ├── ViewController.swift └── Main.storyboard ├── readme.md └── index.cfm /angular-to-coldfusion/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | node_modules 3 | logs 4 | console -------------------------------------------------------------------------------- /html-to-coldfusion/style.css: -------------------------------------------------------------------------------- 1 | button { 2 | opacity: 0; 3 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: giancarlogomez 2 | patreon: giancarlogomez 3 | -------------------------------------------------------------------------------- /chat/subscribers.cfm: -------------------------------------------------------------------------------- 1 | 2 | writeDump(wsGetSubscribers("chat")); 3 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GiancarloGomez/ColdFusion-Realtime-With-WebSockets-Demo-Code/HEAD/favicon.ico -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GiancarloGomez/ColdFusion-Realtime-With-WebSockets-Demo-Code/HEAD/favicon.png -------------------------------------------------------------------------------- /angular-to-coldfusion/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /angular-to-coldfusion/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /angular-to-coldfusion/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GiancarloGomez/ColdFusion-Realtime-With-WebSockets-Demo-Code/HEAD/angular-to-coldfusion/src/favicon.ico -------------------------------------------------------------------------------- /other-demo-files/socket.io_site/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GiancarloGomez/ColdFusion-Realtime-With-WebSockets-Demo-Code/HEAD/other-demo-files/socket.io_site/favicon.ico -------------------------------------------------------------------------------- /other-demo-files/socket.io/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GiancarloGomez/ColdFusion-Realtime-With-WebSockets-Demo-Code/HEAD/other-demo-files/socket.io/public/favicon.ico -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | 3 | Header set Access-Control-Allow-Origin "*" 4 | 5 | RewriteCond %{REQUEST_FILENAME} !-f 6 | RewriteCond %{REQUEST_FILENAME} !-d 7 | RewriteRule ^(.*)/$ $1.cfm [QSA,L] -------------------------------------------------------------------------------- /angular-to-coldfusion/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": [ 8 | "test.ts", 9 | "**/*.spec.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /other-demo-files/socket.io_site/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | 3 | Header set Access-Control-Allow-Origin "*" 4 | Header set Access-Control-Allow-Headers "*" 5 | 6 | RewriteCond %{REQUEST_FILENAME} !-f 7 | RewriteCond %{REQUEST_FILENAME} !-d 8 | RewriteRule ^(.*)/$ $1.cfm [QSA,L] -------------------------------------------------------------------------------- /instructions.md: -------------------------------------------------------------------------------- 1 | ## Server 1 2 | box start server 3 | 4 | ## Server 2 5 | box start ./other-demo-files/socket.io_site/server 6 | 7 | ## Socket IO Server 8 | ./other-demo-files/socket.io/node server 9 | 10 | ## Angular Project 11 | ./angular-to-coldfusion/ng serve 12 | 13 | -------------------------------------------------------------------------------- /other-demo-files/socket.io/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample-socket-io", 3 | "version": "0.0.1", 4 | "description": "My demo socket.io app", 5 | "type": "module", 6 | "dependencies": { 7 | "express": "^4.19.2", 8 | "socket.io": "^4.7.5" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /other-demo-files/socket.io_site/server.json: -------------------------------------------------------------------------------- 1 | { 2 | "app":{ 3 | "cfengine":"lucee" 4 | }, 5 | "web":{ 6 | "gzipEnable":true, 7 | "rewrites":{ 8 | "enable":true, 9 | "config":".htaccess" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /angular-to-coldfusion/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /angular-to-coldfusion/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /angular-to-coldfusion/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /live-demos/demo-00/RestrictedListener.cfc: -------------------------------------------------------------------------------- 1 | component extends="CFIDE.websocket.ChannelListener" { 2 | 3 | public boolean function allowSubscribe(struct subscriberInfo) { 4 | // I am crucial. You must use me. Or else. 5 | if( !arguments.subscriberInfo.connectionInfo.authenticated ) return false; 6 | 7 | return true; 8 | } 9 | 10 | } -------------------------------------------------------------------------------- /chat/ping.cfm: -------------------------------------------------------------------------------- 1 | 2 | param name="url.id" default=""; 3 | 4 | clients = wsGetSubscribers("chat"); 5 | clientIDs = []; 6 | 7 | arrayEach(clients, function(rec){ 8 | arrayAppend(clientIDs,rec.clientID); 9 | }); 10 | 11 | rtn = { "success" : arrayFind(clientIDs,url.id) ? true : false }; 12 | 13 | writeOutput( serializeJSON(rtn) ); 14 | -------------------------------------------------------------------------------- /angular-to-coldfusion/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /server.11.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"realtime-ws-demo-code-@11", 3 | "trayicon":"favicon.png", 4 | "app":{ 5 | "cfengine":"adobe@11.0.19+314546" 6 | }, 7 | "jvm":{ 8 | "heapSize":512, 9 | "minHeapSize":512 10 | }, 11 | "web":{ 12 | "rewrites":{ 13 | "enable":true, 14 | "config":".htaccess" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server.2021.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"realtime-ws-demo-code-@2021", 3 | "trayicon":"favicon.png", 4 | "app":{ 5 | "cfengine":"adobe@2021.0.2+328618" 6 | }, 7 | "jvm":{ 8 | "heapSize":512, 9 | "minHeapSize":512 10 | }, 11 | "web":{ 12 | "rewrites":{ 13 | "enable":true, 14 | "config":".htaccess" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /angular-to-coldfusion/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to websockets!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /live-demos/ping.cfm: -------------------------------------------------------------------------------- 1 | 2 | param name="url.id" default=""; 3 | // get all clients 4 | clients = wsGetSubscribers("demo"); 5 | match = false; 6 | // go thru each until a match is found 7 | for (rec in clients){ 8 | if(!compare(rec.clientid,url.id)){ 9 | match = true; 10 | break; 11 | } 12 | } 13 | // return response 14 | writeOutput(serializeJSON({"success":match})); 15 | -------------------------------------------------------------------------------- /server.2016.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"realtime-ws-demo-code-@2016", 3 | "trayicon":"favicon.png", 4 | "app":{ 5 | "cfengine":"adobe@2016.0.17+325979" 6 | }, 7 | "jvm":{ 8 | "heapSize":512, 9 | "minHeapSize":512 10 | }, 11 | "web":{ 12 | "rewrites":{ 13 | "enable":true, 14 | "config":".htaccess" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server.2018.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"realtime-ws-demo-code-@2018", 3 | "trayicon":"favicon.png", 4 | "app":{ 5 | "cfengine":"adobe@2018.0.19+330149" 6 | }, 7 | "jvm":{ 8 | "heapSize":512, 9 | "minHeapSize":512 10 | }, 11 | "web":{ 12 | "rewrites":{ 13 | "enable":true, 14 | "config":".htaccess" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /angular-to-coldfusion/src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /live-demos/subscribers.cfm: -------------------------------------------------------------------------------- 1 | 2 | topLevelChannels = wsGetAllChannels(); 3 | 4 | for (channel in topLevelChannels){ 5 | 6 | subChannels = wsGetAllChannels(channel); 7 | 8 | if (subChannels.len()){ 9 | for (subChannel in subChannels) 10 | writeDump(label:subChannel,var:wsGetSubscribers(subChannel)) 11 | } else { 12 | writeDump(label:channel,var:wsGetSubscribers(channel)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /angular-to-coldfusion/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Websockets 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /angular-to-coldfusion/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | 14 | -------------------------------------------------------------------------------- /angular-to-coldfusion/src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /server.2023.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"realtime-ws-demo-code-@2023", 3 | "trayEnable":true, 4 | "trayicon":"favicon.png", 5 | "dockEnable":false, 6 | "app":{ 7 | "cfengine":"adobe@2023.0.10+330680" 8 | }, 9 | "jvm":{ 10 | "heapSize":512, 11 | "minHeapSize":512 12 | }, 13 | "web":{ 14 | "rewrites":{ 15 | "enable":true, 16 | "config":".htaccess" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /other-demo-files/socket.io_site/Application.cfc: -------------------------------------------------------------------------------- 1 | component { 2 | this.name = "socketio_demo"; 3 | 4 | function onError(exception,eventName){ 5 | writeOutput(arguments.exception.message); 6 | } 7 | 8 | function onRequestStart(){ 9 | cfheader(name:"Access-Control-Allow-Origin",value:"*"); 10 | cfheader(name:"Access-Control-Allow-Headers",value:"*"); 11 | request.socketio_server = "http://localhost"; 12 | request.socketio_port = 8080; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /angular-to-coldfusion/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule } from '@angular/forms'; 4 | 5 | import { AppComponent } from './app.component'; 6 | 7 | @NgModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | imports: [ 12 | BrowserModule, 13 | FormsModule 14 | ], 15 | providers: [], 16 | bootstrap: [AppComponent] 17 | }) 18 | export class AppModule { } 19 | -------------------------------------------------------------------------------- /angular-to-coldfusion/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "es2015", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "target": "es5", 13 | "typeRoots": [ 14 | "node_modules/@types" 15 | ], 16 | "lib": [ 17 | "es2017", 18 | "dom" 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = false 9 | indent_style = tab 10 | indent_size = 4 11 | tab_width = 4 12 | 13 | [*.js] 14 | indent_style = space 15 | indent_size = 4 16 | tab_width = 4 17 | charset = utf-8 18 | trim_trailing_whitespace = true 19 | insert_final_newline = false 20 | 21 | [*.yml] 22 | indent_style = space 23 | indent_size = 2 24 | 25 | [*.{md,markdown}] 26 | trim_trailing_whitespace = false 27 | insert_final_newline = false -------------------------------------------------------------------------------- /live-demos/assets/css/advanced.css: -------------------------------------------------------------------------------- 1 | #status { 2 | color:#fff; 3 | left:50%; 4 | line-height: 50px; 5 | min-width: 200px; 6 | opacity: 0; 7 | padding: 0 1em; 8 | position: absolute; 9 | text-align: center; 10 | text-transform: uppercase; 11 | top:0; 12 | transform: translateX(-50%); 13 | transition: opacity .25s; 14 | z-index: 100; 15 | } 16 | #status.on {opacity:1;} 17 | #status.connected {background-color: rgba(0,255,0,.5);} 18 | #status.disconnected {background-color: rgba(255,0,0,.5);} 19 | #status.connecting {background-color: rgba(0,0,255,.5);} -------------------------------------------------------------------------------- /other-demo-files/socket.io/public/assets/app.css: -------------------------------------------------------------------------------- 1 | 2 | .btn:focus-visible, 3 | .form-control:focus{ 4 | box-shadow: none; 5 | } 6 | .form-control:focus{ 7 | border-color: var(--bs-border-color); 8 | } 9 | section { 10 | height: calc( 100vh - 3.5625rem ) ; 11 | ul { 12 | flex-grow: 1; 13 | 14 | li { padding : 1rem; } 15 | li:nth-child(odd) { background: #efefef; } 16 | } 17 | } 18 | pre { 19 | font-family: 'JetBrains Mono', monospace; 20 | font-weight: 300; 21 | line-height: 1.42; 22 | margin: 0; 23 | } -------------------------------------------------------------------------------- /live-demos/assets/js/advanced.js: -------------------------------------------------------------------------------- 1 | var statusDiv = document.getElementById('status'); 2 | 3 | AdvancedSocket.disconnected = function(){ 4 | statusDiv.innerHTML = 'Disconnected'; 5 | showStatus('disconnected'); 6 | } 7 | 8 | AdvancedSocket.connected = function(){ 9 | statusDiv.innerHTML = 'Connected'; 10 | showStatus('connected'); 11 | } 12 | 13 | AdvancedSocket.connecting = function(){ 14 | statusDiv.innerHTML = 'Connecting ...'; 15 | showStatus('connecting'); 16 | } 17 | 18 | function showStatus(statusClass){ 19 | statusDiv.className = 'on ' + statusClass; 20 | } -------------------------------------------------------------------------------- /angular-to-coldfusion/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | -------------------------------------------------------------------------------- /angular-to-coldfusion/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /other-demo-files/socket.io_site/info.cfm: -------------------------------------------------------------------------------- 1 | 2 | rtn = {"success":true,"message":""}; 3 | if( !structKeyExists(request,"socketio_server") ) 4 | rtn.message = "You must set the URL to the Socket.IO server - review instructions in Application.onRequestStart()"; 5 | if( !structKeyExists(request,"socketio_port") ) 6 | rtn.message = "You must set the port to your Socket.IO server - review instructions in Application.onRequestStart()"; 7 | rtn.success = len(rtn.message) ? false : true; 8 | if ( rtn.success ){ 9 | rtn["data"] = { 10 | "socketio_server" : request.socketio_server, 11 | "socketio_port" : request.socketio_port 12 | }; 13 | } 14 | cfcontent(reset="true",type="application/json"); 15 | writeOutput( serializeJSON(rtn) ); 16 | -------------------------------------------------------------------------------- /shared/fetch-joke.cfm: -------------------------------------------------------------------------------- 1 | 2 | joke = ""; 3 | try { 4 | categories = [ 5 | "animal", 6 | "career", 7 | "celebrity", 8 | "dev", 9 | // "explicit", 10 | "fashion", 11 | "food", 12 | "history", 13 | "money", 14 | "movie", 15 | "music", 16 | "political", 17 | "religion", 18 | "science", 19 | "sport", 20 | "travel" 21 | ]; 22 | cfhttp( 23 | url = "https://api.chucknorris.io/jokes/random?category=" & categories[ randRange(1,categories.len()) ], 24 | timeout = 5, 25 | result = "apiRequest" 26 | ); 27 | joke = deserializeJSON( apiRequest.fileContent ).value 28 | } 29 | catch ( any e ){ 30 | joke = "We could not fetch a funny joke so this is just the server responding"; 31 | } 32 | -------------------------------------------------------------------------------- /angular-to-coldfusion/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /live-demos/services/communicate.cfc: -------------------------------------------------------------------------------- 1 | component { 2 | remote any function publish(){ 3 | sleep( 1000 ); 4 | return "This is the value returned from publish()"; 5 | } 6 | 7 | remote any function p2p( string value = 5 ){ 8 | if ( !isNumeric( arguments.value ) ) 9 | arguments.value = 5; 10 | cfthread ( action="run", name="backToInvoker_#createUUID()#", total=arguments.value ) { 11 | for ( i = 1; i <= total; i++ ){ 12 | sleep( 500 ); 13 | wsSendMessage( i & " of " & total & " : I am only returning this to the P2P Client" ); 14 | } 15 | } 16 | return "This is the value returned from p2p()"; 17 | } 18 | 19 | remote any function doIt( required string name, required string message ){ 20 | return arguments.name & " says """ & arguments.message & """"; 21 | } 22 | } -------------------------------------------------------------------------------- /html-to-coldfusion/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HTML TO CF 6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 | 18 |
19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /live-demos/demo-01/index.cfm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSocket Demo - Simple 6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 | 18 |
19 | 20 | 21 |
22 | 23 | 24 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /other-demo-files/socket.io_site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Socket.IO Example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 | 22 | 23 |
24 | 25 | -------------------------------------------------------------------------------- /angular-to-coldfusion/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 |

Welcome to {{ title }} on Angular

3 | 4 | 5 |
{{message}}
6 | Angular Logo 7 | 8 | -------------------------------------------------------------------------------- /angular-to-coldfusion/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /Application.cfc: -------------------------------------------------------------------------------- 1 | component { 2 | this.name = "websockets_demo"; 3 | 4 | function onError(exception,eventName){ 5 | writeOutput("
" & arguments.exception.message & "
"); 6 | } 7 | 8 | function onRequestStart(){ 9 | /* 10 | * Set the request parameters to the IIS site running the code in 11 | * /other-demo-files/socket.io_site/ 12 | */ 13 | // request.socketio_server = "http://localhost:{{port}}"; 14 | // request.socketio_broadcast = "https://localhost:{{port}}/broadcast/"; 15 | cfheader(name:"Access-Control-Allow-Origin",value:"*"); 16 | cfheader(name:"Access-Control-Allow-Headers",value:"*"); 17 | request.socketio_server = "http://localhost:51095/"; 18 | request.socketio_broadcast = "http://localhost:51095/broadcast/"; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /other-demo-files/socket.io_site/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /other-demo-files/socket.io/public/assets/app.js: -------------------------------------------------------------------------------- 1 | const socket = io('',{ query: { channels : 'general,chat' }}); 2 | const form = document.getElementById('form'); 3 | const message = document.getElementById('message'); 4 | const messages = document.getElementById('messages'); 5 | 6 | form.addEventListener('submit', event => { 7 | event.preventDefault(); 8 | if ( message.value.toString().trim() ) { 9 | socket.emit('publish',{ channel:'chat', message:message.value}); 10 | message.value = ''; 11 | } 12 | }); 13 | 14 | socket.on( 'message', data => { 15 | const item = document.createElement('li'), 16 | pre = document.createElement('pre'); 17 | 18 | pre.textContent = JSON.stringify( data ).replace(/,"/g,',\n "').replace('{','{\n ').replace('}','\n}'); 19 | 20 | item.appendChild( pre ) 21 | messages.appendChild( item ); 22 | messages.scrollTo( { top:document.body.scrollHeight,left:0,behavior:'smooth' } ); 23 | }); -------------------------------------------------------------------------------- /angular-to-coldfusion/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | *{ 2 | box-sizing: border-box; 3 | } 4 | h1,div,textarea { 5 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 6 | } 7 | h1 { 8 | text-align: center; 9 | font-weight: 300; 10 | } 11 | textarea { 12 | resize: none; 13 | width:100%; 14 | height:3em; 15 | font-size: 14px; 16 | text-align: center; 17 | padding:1em; 18 | line-height: 1; 19 | border:1px solid #ddd; 20 | display: block; 21 | } 22 | button { 23 | width:100%; 24 | border:none; 25 | display: block; 26 | line-height: 44px; 27 | text-transform: uppercase; 28 | text-align: center; 29 | margin: 1em 0; 30 | } 31 | .message_box{ 32 | border:1px solid #ddd; 33 | background-color: #f5f5f5; 34 | min-height: 300px; 35 | display: flex; 36 | align-items: center; 37 | justify-content: center; 38 | color: #444; 39 | padding:20px; 40 | text-align: center; 41 | } 42 | 43 | img { 44 | display: block; 45 | margin:0 auto; 46 | } -------------------------------------------------------------------------------- /html-to-coldfusion/readme.md: -------------------------------------------------------------------------------- 1 | # Connecting without ColdFusion JavaScript 2 | 3 | This is a simple example on how to connect to the WebSockets Server without ColdFusion Team's JavaScript files. 4 | 5 | The WebSocket API is simple and straight forward but subscribing, unsubscribing, authenticating, etc is all covered in the ColdFusion JS files as there is custom data that is prepared and sent to the server from the client. 6 | 7 | In this example I created a cfwebsocket.js file that prepares the object necessary for subscribing, unsubscribing and publishing messages to a channel. 8 | 9 | The ColdFusion Team's JavaScript files where I was able to reverse engineer this from are: 10 | * /CFIDE/scripts/ajax/package/cfwebsocketCore.js 11 | * /CFIDE/scripts/ajax/package/cfwebsocketChannel.js 12 | 13 | __REQUIRED SETUP IN script.js__ 14 | Make sure to edit the parameters when creating the connection based on the server and ports you are running. 15 | 16 | __IMPORTANT__ 17 | Please make sure to run the first example under `../live-demos/demo-01/` as this is the application used. -------------------------------------------------------------------------------- /angular-to-coldfusion/src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /other-demo-files/socket.io/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Socket.IO Chat 6 | 7 | 8 | 9 | 10 |
11 |

Socket.IO Chat

12 |
13 |
    14 |
    15 |
    16 | 17 | 18 |
    19 |
    20 |
    21 |
    22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Giancarlo Gomez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /angular-to-coldfusion/README.md: -------------------------------------------------------------------------------- 1 | # Websockets 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.2.4. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /angular-to-coldfusion/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async(() => { 6 | TestBed.configureTestingModule({ 7 | declarations: [ 8 | AppComponent 9 | ], 10 | }).compileComponents(); 11 | })); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.debugElement.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'websockets'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.debugElement.componentInstance; 22 | expect(app.title).toEqual('websockets'); 23 | }); 24 | 25 | it('should render title in a h1 tag', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.debugElement.nativeElement; 29 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to websockets!'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /live-demos/Application.cfc: -------------------------------------------------------------------------------- 1 | component { 2 | this.name = "websockets_2021_demo"; 3 | this.sessionmanagement = true; 4 | this.sessiontimeout = createTimeSpan(1,0,0,0); 5 | this.serialization.preservecaseforstructkey = true; 6 | 7 | this.wschannels = [ 8 | {name:"demo"}, 9 | {name:"conference"}, 10 | {name:"restricted",cfclistener:"demo-00.RestrictedListener"} 11 | ]; 12 | 13 | public void function onRequestStart() { 14 | if (structKeyExists(url, "reload")){ 15 | wsPublish("demo","FORCE-RECONNECT"); 16 | applicationStop(); 17 | location(cgi.script_name,false); 18 | } 19 | } 20 | 21 | public void function onError(exception,eventName){ 22 | writeDump(arguments.exception); 23 | } 24 | 25 | public boolean function onWSAuthenticate(string username, string password, struct connectionInfo) { 26 | var usersAllowed = ["JC","Maria","Mailang","Jonah","Gia"]; 27 | var authenticated = arrayFindNoCase(usersAllowed,arguments.username); 28 | if (authenticated){ 29 | arguments.connectionInfo.authenticated = true; 30 | arguments.connectionInfo.username = arguments.username; 31 | } else { 32 | arguments.connectionInfo.authenticated = false; 33 | } 34 | return authenticated ; 35 | } 36 | } -------------------------------------------------------------------------------- /html-to-coldfusion/script.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This example is based on the application found under the /live-demos/demo-01/Application.cfc 3 | * which is where the channel and appName (Application name) comes from 4 | * The objects defined as subscribe,unsubscribe and message is how the WebSocket server expects to receive data in 5 | * a send() call. 6 | */ 7 | var cfws = new cfwebsocket( 8 | 'websockets_demo1', // appName 9 | 'demo', // channel 10 | 'http://127.0.0.1:50320/live-demos/demo-01/runme/', // appStartPath 11 | parseMessage, // onMessage 12 | subscribed, // onSubscribe 13 | false, // isProxy 14 | 8581, // wsPort 15 | '127.0.0.1', // server 16 | false, // secure, 17 | {device:'Mobile'} // customOptions 18 | ); 19 | 20 | // overwrite from /assets/js/script.js 21 | sendMessage.removeEventListener('click',doSendMessage); 22 | sendMessage.addEventListener('click',publish); 23 | 24 | function publish(){ 25 | if (cfws.publish(messageText.value.trim())) 26 | doToggleMessageForm(); 27 | } 28 | 29 | function subscribed(){ 30 | // lets show our buttons 31 | Array.from(document.getElementsByTagName('button')).forEach(function(button){ 32 | button.style.opacity = 1; 33 | button.disabled = false; 34 | }); 35 | } -------------------------------------------------------------------------------- /angular-to-coldfusion/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'WebSockets' 10 | message = '...' 11 | cfwebsocket:any; 12 | user_message:string = '' 13 | 14 | constructor() { 15 | if (typeof window['cfwebsocket'] === 'function'){ 16 | this.cfwebsocket = new window['cfwebsocket']( 17 | 'websockets_demo1', // appName 18 | 'demo', // channel 19 | 'http://localhost:50320/live-demos/demo-01/runme/', // appStartPath 20 | this, // onMessage 21 | this, // onSubscribe 22 | false, // isProxy 23 | 8581, // wsPort 24 | 'localhost', // server 25 | false // secure 26 | ) 27 | } 28 | } 29 | 30 | parseMessage(msg){ 31 | if (msg.data) 32 | this.message = msg.data; 33 | } 34 | 35 | subscribed(){ 36 | this.message = 'We are subscribed!' 37 | } 38 | 39 | publish(){ 40 | if (this.cfwebsocket.publish(this.user_message.trim())) 41 | this.user_message = ''; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /live-demos/demo-00/index.cfm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebSocket Simple Example 8 | 9 | 10 | 11 | 12 |
    13 | #application.applicationname# 14 | Simple Example
    Everything is in the console
    15 |
    16 |
    17 | 18 | 25 | 39 | 40 | -------------------------------------------------------------------------------- /live-demos/demo-01/advanced.cfm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSocket Demo - Advanced 6 | 7 | 8 | 9 | 16 | 17 | 23 | 24 | 25 | 26 |
    27 | 28 | 29 |
    30 | 31 | 32 | 33 | 34 | 35 | 40 | 41 | -------------------------------------------------------------------------------- /angular-to-coldfusion/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "websockets", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^6.1.0", 15 | "@angular/common": "^6.1.0", 16 | "@angular/compiler": "^6.1.0", 17 | "@angular/core": "^6.1.0", 18 | "@angular/forms": "^6.1.0", 19 | "@angular/http": "^6.1.0", 20 | "@angular/platform-browser": "^6.1.0", 21 | "@angular/platform-browser-dynamic": "^6.1.0", 22 | "@angular/router": "^6.1.0", 23 | "core-js": "^2.5.4", 24 | "rxjs": "~6.2.0", 25 | "zone.js": "~0.8.26" 26 | }, 27 | "devDependencies": { 28 | "@angular-devkit/build-angular": "~0.8.0", 29 | "@angular/cli": "~6.2.4", 30 | "@angular/compiler-cli": "^6.1.0", 31 | "@angular/language-service": "^6.1.0", 32 | "@types/jasmine": "~2.8.8", 33 | "@types/jasminewd2": "~2.0.3", 34 | "@types/node": "~8.9.4", 35 | "codelyzer": "~4.3.0", 36 | "jasmine-core": "~2.99.1", 37 | "jasmine-spec-reporter": "~4.2.1", 38 | "karma": "~3.0.0", 39 | "karma-chrome-launcher": "~2.2.0", 40 | "karma-coverage-istanbul-reporter": "~2.0.1", 41 | "karma-jasmine": "~1.1.2", 42 | "karma-jasmine-html-reporter": "^0.2.2", 43 | "protractor": "~5.4.0", 44 | "ts-node": "~7.0.0", 45 | "tslint": "~5.11.0", 46 | "typescript": "~2.9.2" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /live-demos/demo-00/advanced.cfm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebSocket Advanced Example 8 | 9 | 10 | 19 | 20 | 21 |
    22 | #application.applicationname# 23 | Advanced Example
    Everything is in the console
    24 |
    25 |
    26 | 27 | 28 | 29 | 34 | 35 | 40 | 41 | -------------------------------------------------------------------------------- /live-demos/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | HTML Test 8 | 9 | 10 | 11 |
    12 | HTML Example 13 | Nothing to see here
    Everything is in the console
    14 |
    15 | 16 | 46 | 47 | -------------------------------------------------------------------------------- /chat/Application.cfc: -------------------------------------------------------------------------------- 1 | component { 2 | this.name = "webSocketsChat"; 3 | this.sessionmanagement = true; 4 | this.sessiontimeout = createTimeSpan(1,0,0,0); 5 | this.serialization.preservecaseforstructkey = true; 6 | 7 | // websockets 8 | this.wschannels = [ 9 | {name:"chat",cfclistener:"ChatListener"} 10 | ]; 11 | 12 | public boolean function onApplicationStart(){ 13 | application.timestamp = getHttpTimeString(); 14 | application.publishedMessages = 0; 15 | application.publishedPreviousMessages = 0; 16 | return true; 17 | } 18 | 19 | public boolean function onRequestStart(targetPage){ 20 | if (structKeyExists(url,"reload")){ 21 | // tell everyone to reconnect ( AdvancedSocket Feature ) 22 | WsPublish("chat","FORCE-RECONNECT"); 23 | applicationStop(); 24 | location('./',false); 25 | } 26 | return true; 27 | } 28 | 29 | public boolean function onWSAuthenticate(string username, string password, struct connectionInfo) { 30 | // Demo User Authentication - very simple here you would do real work 31 | var usersAllowed = ["Admin","JC","Maria","Mailang","Jonah","Gia"]; 32 | var authenticated = arrayFindNoCase(usersAllowed,arguments.username); 33 | if (authenticated){ 34 | arguments.connectionInfo.authenticated = true; 35 | arguments.connectionInfo.username = arguments.username; 36 | } else { 37 | connectionInfo.authenticated = false; 38 | } 39 | return authenticated; 40 | } 41 | } -------------------------------------------------------------------------------- /live-demos/demo-00/restricted.cfm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebSocket Example Auth 8 | 9 | 10 | 11 | 12 |
    13 | #application.applicationname# 14 | Restricted Example
    Everything is in the console
    15 |
    16 |
    17 | 18 | 24 | 53 | 54 | -------------------------------------------------------------------------------- /chat/broadcast.cfm: -------------------------------------------------------------------------------- 1 | 2 | // this stops an OPTIONS request from continuing to process 3 | if ( !compare(cgi.request_method,"OPTIONS") ) 4 | exit; 5 | 6 | isAjax = !compareNoCase(cgi["X-Requested-With"],"XMLHttpRequest"); 7 | threadName = "ws_msg_" & createUUID(); 8 | 9 | params = { 10 | "channel" : "chat", 11 | "message" : url.message ?: ( form.message ?: "" ) 12 | }; 13 | 14 | asJSON = val( url.json ?: ( form.json ?: 0 ) ); 15 | 16 | if ( !params.message.len() ){ 17 | include "../shared/fetch-joke.cfm"; 18 | params.message = joke; 19 | } 20 | // remove HTML entities from string because it bombs out 21 | params.message = params.message .replaceAll("\&[^\;]+\;",""); 22 | // publish via thread, specially helpful when using Frameworks or calling in the middle of an Ajax Request 23 | cfthread( action="run",name=threadName,params=params,asJSON=asJSON){ 24 | wsPublish( 25 | attributes.params.channel, 26 | attributes.asJSON ? serializeJSON(attributes.params) : attributes.params.message 27 | ); 28 | } 29 | // for XHR response 30 | if ( isAjax ){ 31 | writeOutput( params.message ); 32 | exit; 33 | } 34 | 35 | 36 | 37 | 38 | 39 | 40 | Message Posted 41 | 42 | 43 | 44 |
    45 | The message you posted is as follows
    46 | #params.message# 47 |
    48 | 49 | 50 |
    -------------------------------------------------------------------------------- /live-demos/broadcast.cfm: -------------------------------------------------------------------------------- 1 | 2 | // this stops an OPTIONS request from continuing to process 3 | if ( !compare(cgi.request_method,"OPTIONS") ) 4 | exit; 5 | 6 | isAjax = !compareNoCase(cgi["X-Requested-With"],"XMLHttpRequest"); 7 | threadName = "ws_msg_" & createUUID(); 8 | 9 | params = { 10 | "channel" : lcase( url.channel ?: ( form.channel ?: "demo" ) ), 11 | "message" : url.message ?: ( form.message ?: "" ) 12 | }; 13 | 14 | asJSON = val( url.json ?: ( form.json ?: 0 ) ); 15 | 16 | if ( !params.message.len() ){ 17 | include "../shared/fetch-joke.cfm"; 18 | params.message = joke; 19 | } 20 | // remove HTML entities from string because it bombs out 21 | params.message = params.message .replaceAll("\&[^\;]+\;",""); 22 | // publish via thread, specially helpful when using Frameworks or calling in the middle of an Ajax Request 23 | cfthread( action="run",name=threadName,params=params,asJSON=asJSON){ 24 | wsPublish( 25 | attributes.params.channel, 26 | attributes.asJSON ? serializeJSON(attributes.params) : attributes.params.message 27 | ); 28 | } 29 | // for XHR response 30 | if ( isAjax ){ 31 | writeOutput( params.message ); 32 | exit; 33 | } 34 | 35 | 36 | 37 | 38 | 39 | 40 | Message Posted 41 | 42 | 43 | 44 |
    45 | The message you posted is as follows
    46 | #params.message# 47 |
    48 | 49 | 50 |
    -------------------------------------------------------------------------------- /other-demo-files/socket.io_site/broadcast.cfm: -------------------------------------------------------------------------------- 1 | 2 | if( !structKeyExists(request,"socketio_server") ) 3 | throw("You must set the URL to the Socket.IO server - review instructions in Application.onRequestStart()"); 4 | if( !structKeyExists(request,"socketio_port") ) 5 | throw("You must set the port to your Socket.IO server - review instructions in Application.onRequestStart()"); 6 | 7 | // this stops an OPTIONS request from continuing to process 8 | if ( !compare(cgi.request_method,"OPTIONS") ) 9 | exit; 10 | 11 | isAjax = !compareNoCase(cgi["X-Requested-With"] ?: "","XMLHttpRequest"); 12 | 13 | params = { 14 | "channel" : lcase( url.channel ?: ( form.channel ?: "demo" ) ), 15 | "message" : url.message ?: ( form.message ?: "" ) 16 | }; 17 | 18 | asJSON = val( url.json ?: ( form.json ?: 0 ) ); 19 | 20 | if ( !params.message.len() ){ 21 | include "../shared/fetch-joke.cfm"; 22 | params.message = joke; 23 | } 24 | // remove HTML entities from string because it bombs out 25 | params.message = params.message.replaceAll("\&[^\;]+\;",""); 26 | // create new request 27 | cfhttp( 28 | url = request.socketio_server & "/publish", 29 | method = "POST", 30 | port = request.socketio_port 31 | ){ 32 | cfhttpparam(type:"formField",name:"channel",value:params.channel); 33 | cfhttpparam(type:"formField",name:"message",value: asJSON ? serializeJSON(params) : params.message); 34 | } 35 | // for XHR response 36 | if ( isAjax ){ 37 | writeOutput( params.message ); 38 | exit; 39 | } 40 | 41 | 42 | 43 | 44 | 45 | 46 | Message Posted 47 | 48 | 49 | 50 |
    51 | The message you posted is as follows
    52 | #params.message# 53 |
    54 | 55 | 56 |
    -------------------------------------------------------------------------------- /chat/assets/chat.css: -------------------------------------------------------------------------------- 1 | :root{ 2 | --font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 3 | } 4 | body{ 5 | font-family:var(--font-family) !important; 6 | height:100vh; 7 | min-height:400px; 8 | } 9 | main { 10 | border:1px solid #ddd; 11 | border-width: 1px 0; 12 | height:calc(100vh - 13em); 13 | } 14 | #messages, 15 | #users{ 16 | margin: 0; 17 | overflow-y:scroll; 18 | } 19 | #users{ 20 | width:200px; 21 | border-right:1px solid #ddd; 22 | } 23 | #users ul{ 24 | margin:0; 25 | padding-left: 0; 26 | list-style: none; 27 | } 28 | #users ul li { 29 | background-color:rgba(0,0,0,.1); 30 | padding:.5em; 31 | margin: 2px; 32 | } 33 | #users ul li.me{ 34 | background-color:rgba(25, 135, 84,.1); 35 | } 36 | #users ul small{ 37 | display: block; 38 | color: #555; 39 | font-size: .875em; 40 | } 41 | #chat-message-form{ 42 | bottom: 52px; 43 | padding:10px; 44 | } 45 | #status-message{ 46 | border-width: 1px 0 0 0; 47 | margin:0; 48 | } 49 | /* view state */ 50 | .navbar, 51 | main, 52 | #chat-message-form, 53 | #status-message{ 54 | opacity: 1; 55 | transition:opacity .5s; 56 | } 57 | body.off .navbar, 58 | body.off main, 59 | body.off #chat-message-form, 60 | body.off #status-message{ 61 | opacity: 0; 62 | } 63 | /* message styles */ 64 | .message{ 65 | background-color:#eee; 66 | margin:3px; 67 | border-radius: .4em; 68 | border:1px solid #ddd; 69 | overflow: hidden; 70 | } 71 | .message .sender{ 72 | background-color: rgba(255,255,255,.7); 73 | padding: .3em; 74 | color:#888; 75 | font-size:.9em; 76 | } 77 | .message .content{ 78 | padding: .3em; 79 | } 80 | .message.me{ 81 | background-color: #b5d5f8; 82 | border-color: #a6c4e4; 83 | } 84 | .message.me .sender{ 85 | color: #7C8DA5; 86 | } 87 | .message.system{ 88 | background-color: #deefd8; 89 | border-color: #d6e7c6; 90 | } 91 | .message.welcome{ 92 | padding: .3em; 93 | color: #664d03; 94 | background-color: #fff3cd; 95 | border-color: #ffecb5; 96 | } 97 | .message.private{ 98 | background-color: #f8d7da; 99 | border-color: #f5c2c7; 100 | } 101 | .message.private .sender{ 102 | color: #842029; 103 | } 104 | -------------------------------------------------------------------------------- /other-demo-files/socket.io_site/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin:0; 3 | padding: 51px 0 0; 4 | } 5 | body, button, textarea { 6 | font: 400 14px/1.4em -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 7 | } 8 | pre{ 9 | font: 400 14px/1.4em 'Operator Mono Ssm', Menlo, Consolas, monospace; 10 | border-radius: 0; 11 | background-color: transparent; 12 | margin:0; 13 | border:0; 14 | border-bottom:1px solid #ddd; 15 | padding:9.5px 220px 9.5px 9.5px; 16 | white-space: pre-wrap; 17 | min-height: 4em; 18 | display: flex; 19 | align-items: center; 20 | } 21 | * { 22 | box-sizing: border-box; 23 | } 24 | nav { 25 | background-color: #f8f8f8; 26 | border-bottom:1px solid #e7e7e7; 27 | padding:0 15px; 28 | position: fixed; 29 | width:100%; 30 | top:0; 31 | text-align: right; 32 | z-index: 1; 33 | } 34 | nav span, 35 | button { 36 | background-color:transparent; 37 | border: none; 38 | color: #777; 39 | cursor:pointer; 40 | line-height: 50px; 41 | outline: none; 42 | padding:0 15px; 43 | } 44 | nav span { 45 | display: inline-block; 46 | float: left; 47 | padding: 0; 48 | font-size: 1.2em; 49 | } 50 | .hide{ 51 | display: none; 52 | } 53 | 54 | /* CONSOLE */ 55 | #console { 56 | bottom:0; 57 | list-style: none; 58 | margin:0; 59 | padding:0; 60 | } 61 | #console li { 62 | position: relative; 63 | opacity: .5; 64 | transition: opacity .25s; 65 | } 66 | #console li:nth-child(even){ 67 | background-color: #f5f5f5; 68 | } 69 | #console li:last-child { opacity: 1; } 70 | #console li strong { 71 | position: absolute; 72 | right:1em; 73 | top:1em; 74 | font-size: 1.2em; 75 | font-weight: 600; 76 | color: #888; 77 | } 78 | 79 | /* FORM MESSAGE */ 80 | form#message{ 81 | position: fixed; 82 | left:50%; 83 | top:50%; 84 | background-color: #f0f0f0; 85 | padding:10px; 86 | border:1px solid #ddd; 87 | box-shadow: 0 0 4px rgba(0,0,0,.5); 88 | transform: translate(-50%,-50%); 89 | } 90 | form#message textarea{ 91 | border:1px solid #ddd; 92 | display: block; 93 | height:200px; 94 | width:400px; 95 | margin-bottom: 5px; 96 | padding: 10px; 97 | resize: none; 98 | } 99 | form#message button { 100 | width:100%; 101 | display: block; 102 | text-align: center; 103 | background-color: #ddd; 104 | } 105 | form#message button:active{ 106 | background-color: #ccc; 107 | } -------------------------------------------------------------------------------- /live-demos/assets/js/script.js: -------------------------------------------------------------------------------- 1 | // get our console and see if we are using firefox 2 | var _console = document.getElementById('console'), 3 | _scroll = navigator.userAgent.indexOf('Firefox') !== -1, 4 | // message controls 5 | openMessage = document.getElementById('domessage'), 6 | openMessage = document.getElementById('domessage'), 7 | sendMessage = document.getElementById('sendmessage'), 8 | messageForm = document.getElementById('message'), 9 | messageText = document.getElementById('messagetext'); 10 | 11 | // clear our log 12 | document.getElementById('clearlog').addEventListener('click',function(e){ 13 | e.preventDefault(); 14 | _console.innerHTML = ''; 15 | }); 16 | 17 | // parse message when received thru websocket 18 | function parseMessage(message){ 19 | var _date = new Date(), 20 | // format timestamp to a readable format 21 | _ts = _date.toLocaleDateString() + ' ' + _date.toLocaleTimeString(), 22 | // format message object into multiline readable string 23 | _data = JSON.stringify(message).replace(/,"/g,',\n "').replace('{','{\n ').replace('}','\n}'), 24 | // create new element 25 | _li = document.createElement('li'); 26 | // set the text 27 | _li.innerHTML = `${_ts}
    ${_data}
    `; 28 | // append element to console 29 | _console.appendChild(_li); 30 | 31 | // extra 32 | if (document.body.scrollIntoView) 33 | document.body.scrollIntoView({block: "end", behavior: "smooth"}); 34 | } 35 | 36 | // Event Listeners on Buttons and Textarea 37 | openMessage.addEventListener('click',doToggleMessageForm); 38 | sendMessage.addEventListener('click',doSendMessage); 39 | messageText.addEventListener('keyup',function(event){ 40 | if(event.keyCode === 13 && event.shiftKey === true) 41 | doSendMessage(); 42 | }); 43 | 44 | // Toggle our message modal 45 | function doToggleMessageForm(){ 46 | if ( messageForm.classList.contains('hide') === true){ 47 | messageForm.classList.remove('hide'); 48 | messageForm.message.value = ''; 49 | openMessage.innerHTML = '× MESSAGE'; 50 | } else { 51 | messageForm.classList.add('hide'); 52 | openMessage.innerHTML = 'MESSAGE'; 53 | } 54 | } 55 | 56 | // Send a message to the server => publish() 57 | function doSendMessage(){ 58 | var theMessage = messageText.value.trim(); 59 | if (theMessage !== ''){ 60 | ws.publish('demo',theMessage); 61 | doToggleMessageForm(); 62 | } 63 | } -------------------------------------------------------------------------------- /live-demos/assets/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin:0; 3 | padding: 51px 0 0; 4 | } 5 | body, button, textarea { 6 | font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 7 | } 8 | pre{ 9 | font: 400 1rem/1.42 'JetBrains Mono','Operator Mono Ssm', Menlo, Consolas, monospace; 10 | border-radius: 0; 11 | background-color: transparent; 12 | margin:0; 13 | border:0; 14 | border-bottom:1px solid #ddd; 15 | padding:9.5px; 16 | white-space: pre-wrap; 17 | } 18 | * { 19 | box-sizing: border-box; 20 | } 21 | #sig { 22 | position: fixed; 23 | bottom:0; 24 | width: 100vw; 25 | background-color: #eee; 26 | border-top:1px solid #ddd; 27 | padding: 1em; 28 | margin:0; 29 | text-align: center; 30 | } 31 | body table[class^=cfdump]{ 32 | font-family: 'JetBrains Mono','Operator Mono Ssm',Menlo,Consolas, monospace; 33 | font-size: .875rem; 34 | line-height: 1.42; 35 | } 36 | nav:not(.navbar) { 37 | background-color: #f8f8f8; 38 | border-bottom:1px solid #e7e7e7; 39 | padding:0 15px; 40 | position: fixed; 41 | width:100%; 42 | top:0; 43 | text-align: right; 44 | z-index: 1; 45 | } 46 | nav:not(.navbar) span, 47 | nav:not(.navbar) button:not(.btn) { 48 | background-color:transparent; 49 | border: none; 50 | color: #777; 51 | cursor:pointer; 52 | line-height: 50px; 53 | outline: none; 54 | padding:0 15px; 55 | } 56 | .container-fluid button:not(.btn){ 57 | border-left:1px solid #ddd; 58 | } 59 | nav:not(.navbar) span { 60 | display: inline-block; 61 | float: left; 62 | padding: 0; 63 | font-size: 1.2em; 64 | } 65 | nav.navbar{ 66 | margin-bottom: 20px; 67 | } 68 | .hide{ 69 | display: none; 70 | } 71 | 72 | /* CONSOLE */ 73 | #console { 74 | bottom:0; 75 | list-style: none; 76 | margin:0; 77 | padding:0; 78 | } 79 | #console li { 80 | position: relative; 81 | opacity: .5; 82 | transition: opacity .25s; 83 | } 84 | #console li:nth-child(even){ 85 | background-color: #f5f5f5; 86 | } 87 | #console li:last-child { opacity: 1; } 88 | #console li strong { 89 | position: absolute; 90 | right:1em; 91 | top:1em; 92 | font-size: 1.2em; 93 | font-weight: 600; 94 | color: #888; 95 | } 96 | 97 | /* FORM MESSAGE */ 98 | form#message{ 99 | position: fixed; 100 | left:50%; 101 | top:50%; 102 | background-color: #f0f0f0; 103 | padding:10px; 104 | border:1px solid #ddd; 105 | box-shadow: 0 0 4px rgba(0,0,0,.5); 106 | transform: translate(-50%,-50%); 107 | } 108 | form#message textarea{ 109 | border:1px solid #ddd; 110 | display: block; 111 | height:200px; 112 | width:400px; 113 | margin-bottom: 5px; 114 | padding: 10px; 115 | resize: none; 116 | } 117 | form#message button { 118 | width:100%; 119 | display: block; 120 | text-align: center; 121 | background-color: #ddd; 122 | } 123 | form#message button:active{ 124 | background-color: #ccc; 125 | } -------------------------------------------------------------------------------- /angular-to-coldfusion/src/assets/cfwebsocket.js: -------------------------------------------------------------------------------- 1 | function cfwebsocket ( 2 | appName, 3 | channel, 4 | appStartPath, 5 | onMessage, 6 | onSubscribe, 7 | isProxy, 8 | wsPort, 9 | server, 10 | secure 11 | ){ 12 | var me = this; 13 | 14 | this.server = server ? server : location.hostname; 15 | this.isSecure = secure ? secure : (location.protocol === 'https:' ? true : false); 16 | this.protocol = this.isSecure ? 'wss' : 'ws'; 17 | this.port = wsPort ? wsPort : (isProxy ? '' : (this.isSecure ? 8543 : 8577)); 18 | this.path = isProxy ? '/cfws/' : '/cfusion/cfusion'; 19 | this.server = `${this.protocol}://${this.server}${this.port ? `:${this.port}` : ''}`; 20 | this.ws = new WebSocket(`${this.server}${this.path}`); 21 | 22 | // cf json properties for sending messages 23 | this.subscribe = {ns: 'coldfusion.websocket.channels', type: 'subscribe', channel: channel, appName: appName}, 24 | this.unsubscribe = {ns: 'coldfusion.websocket.channels', type: 'unsubscribe', channel: channel, appName: appName}, 25 | this.message = {ns: 'coldfusion.websocket.channels', type: 'publish', channel: channel, data:'', appName: appName}; 26 | 27 | // WebSocket API Events 28 | this.ws.onopen = function(e){ 29 | me.subscribeMe(); 30 | }; 31 | 32 | this.ws.onmessage = function(e){ 33 | // when we receive a message pass it to the parseMessage function (the data piece of the object received) 34 | // console.log('onmessage',e); 35 | var data = JSON.parse(e.data); 36 | // looks like we are calling before CF has been executed 37 | if (data.code === -1 && data.msg.indexOf('is not running') !== -1){ 38 | if (appStartPath){ 39 | fetch(appStartPath,{ 40 | method : 'GET' 41 | }) 42 | .then( response => response.json() ) 43 | .then( success => { 44 | if (success) 45 | me.subscribeMe(); 46 | }); 47 | } 48 | } 49 | else if (data.code === 0 && data.type === 'response' && data.reqType === 'subscribe' && data.msg ==='ok' && onSubscribe && typeof onSubscribe === 'object'){ 50 | onSubscribe.subscribed(); 51 | } 52 | // pass to our onMessage function we passed in 53 | if (onMessage && typeof onMessage === 'object'){ 54 | onMessage.parseMessage(data); 55 | } 56 | }; 57 | 58 | this.ws.onerror = function(e){ 59 | // console.log('onerror',e); 60 | }; 61 | 62 | this.ws.onclose = function(e){ 63 | // console.log('close',e); 64 | }; 65 | 66 | this.subscribeMe = function(){ 67 | if (this.ws.readyState === this.ws.OPEN) 68 | this.ws.send(JSON.stringify(this.subscribe)); 69 | } 70 | 71 | this.unsubscribeMe = function(){ 72 | if (this.ws.readyState === this.ws.OPEN) 73 | this.ws.send(JSON.stringify(this.unsubscribe)); 74 | } 75 | 76 | this.publish = function(value){ 77 | var obj = Object.assign({},this.message), 78 | publish = value !== ''; 79 | if (publish){ 80 | obj.data = value; 81 | this.ws.send(JSON.stringify(obj)); 82 | } 83 | return publish; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /swift-to-coldfusion/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // WebSockets 4 | // 5 | // Created by Giancarlo Gomez on 9/29/18. 6 | // Copyright © 2018 Giancarlo Gomez. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Starscream 11 | 12 | 13 | final class ViewController: UIViewController{ 14 | var socket: WebSocket! 15 | var _subscribe = "{\"ns\":\"coldfusion.websocket.channels\",\"type\":\"subscribe\",\"channel\":\"demo\",\"appName\":\"websockets_demo1\"}"; 16 | 17 | @IBOutlet weak var message: UILabel! 18 | @IBOutlet weak var messageBox: UITextField! 19 | @IBOutlet weak var sendButton: UIButton! 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | var request = URLRequest(url: URL(string: "ws://localhost:8581/cfusion/cfusion")!) 24 | request.timeoutInterval = 5 25 | socket = WebSocket(request: request) 26 | socket.delegate = self 27 | socket.connect() 28 | } 29 | 30 | // MARK: Write Text Action 31 | 32 | @IBAction func write(_ sender: UIButton) { 33 | if (messageBox.text != ""){ 34 | let message = "{\"ns\":\"coldfusion.websocket.channels\",\"type\":\"publish\",\"channel\":\"demo\",\"appName\":\"websockets_demo1\",\"data\":\"\(messageBox.text ?? "")\"}"; 35 | sendMessage(message: message) 36 | messageBox.text = "" 37 | } 38 | } 39 | 40 | } 41 | 42 | // MARK: - WebSocketDelegate 43 | extension ViewController : WebSocketDelegate { 44 | 45 | func sendMessage(message: String){ 46 | socket.write(string: message) 47 | } 48 | 49 | func websocketDidConnect(socket: WebSocketClient) { 50 | sendMessage(message: _subscribe) 51 | } 52 | 53 | func websocketDidDisconnect(socket: WebSocketClient, error: Error?) { 54 | if let e = error as? WSError { 55 | print("websocket is disconnected: \(e.message)") 56 | } else if let e = error { 57 | print("websocket is disconnected: \(e.localizedDescription)") 58 | } else { 59 | print("websocket disconnected") 60 | } 61 | message.text = "WebSocket Server Disconnected" 62 | } 63 | 64 | func websocketDidReceiveMessage(socket: WebSocketClient, text: String) { 65 | // let try and parse received JSON 66 | guard let data = text.data(using: .utf16), 67 | let jsonData = try? JSONSerialization.jsonObject(with: data), 68 | let jsonDict = jsonData as? [String: Any], 69 | let messageType = jsonDict["type"] as? String else { 70 | return 71 | } 72 | // If we are receiving a data type then lets update the text message 73 | if messageType == "data", 74 | let messageData = jsonDict["data"] as? String{ 75 | message.text = messageData 76 | } 77 | // when we subscribe 78 | else if messageType == "response", 79 | let reqType = jsonDict["reqType"] as? String{ 80 | if (reqType == "subscribe"){ 81 | message.text = "WebSocket Server Connected and Subscribed" 82 | } 83 | } 84 | // log the full response 85 | print(text); 86 | } 87 | 88 | func websocketDidReceiveData(socket: WebSocketClient, data: Data) { 89 | print("Received data: \(data.count)") 90 | } 91 | } 92 | 93 | -------------------------------------------------------------------------------- /angular-to-coldfusion/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** IE10 and IE11 requires the following for the Reflect API. */ 41 | // import 'core-js/es6/reflect'; 42 | 43 | 44 | /** Evergreen browsers require these. **/ 45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 46 | import 'core-js/es7/reflect'; 47 | 48 | 49 | /** 50 | * Web Animations `@angular/platform-browser/animations` 51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 53 | **/ 54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 55 | 56 | /** 57 | * By default, zone.js will patch all possible macroTask and DomEvents 58 | * user can disable parts of macroTask/DomEvents patch by setting following flags 59 | */ 60 | 61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 64 | 65 | /* 66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 68 | */ 69 | // (window as any).__Zone_enable_cross_context_check = true; 70 | 71 | /*************************************************************************************************** 72 | * Zone JS is required by default for Angular itself. 73 | */ 74 | import 'zone.js/dist/zone'; // Included with Angular CLI. 75 | 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | -------------------------------------------------------------------------------- /angular-to-coldfusion/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-redundant-jsdoc": true, 69 | "no-shadowed-variable": true, 70 | "no-string-literal": false, 71 | "no-string-throw": true, 72 | "no-switch-case-fall-through": true, 73 | "no-trailing-whitespace": true, 74 | "no-unnecessary-initializer": true, 75 | "no-unused-expression": true, 76 | "no-use-before-declare": true, 77 | "no-var-keyword": true, 78 | "object-literal-sort-keys": false, 79 | "one-line": [ 80 | true, 81 | "check-open-brace", 82 | "check-catch", 83 | "check-else", 84 | "check-whitespace" 85 | ], 86 | "prefer-const": true, 87 | "quotemark": [ 88 | true, 89 | "single" 90 | ], 91 | "radix": true, 92 | "semicolon": [ 93 | true, 94 | "always" 95 | ], 96 | "triple-equals": [ 97 | true, 98 | "allow-null-check" 99 | ], 100 | "typedef-whitespace": [ 101 | true, 102 | { 103 | "call-signature": "nospace", 104 | "index-signature": "nospace", 105 | "parameter": "nospace", 106 | "property-declaration": "nospace", 107 | "variable-declaration": "nospace" 108 | } 109 | ], 110 | "unified-signatures": true, 111 | "variable-name": false, 112 | "whitespace": [ 113 | true, 114 | "check-branch", 115 | "check-decl", 116 | "check-operator", 117 | "check-separator", 118 | "check-type" 119 | ], 120 | "no-output-on-prefix": true, 121 | "use-input-property-decorator": true, 122 | "use-output-property-decorator": true, 123 | "use-host-property-decorator": true, 124 | "no-input-rename": true, 125 | "no-output-rename": true, 126 | "use-life-cycle-interface": true, 127 | "use-pipe-transform-interface": true, 128 | "component-class-suffix": true, 129 | "directive-class-suffix": true 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /chat/index.cfm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | WebSocket Chat 7 | 8 | 9 | 10 | 11 | 18 | 19 | 20 | 31 | 32 | 33 |
    34 | 35 |
    36 | 37 |
    38 |
    39 | 40 | 41 |
    42 |
    43 |
    44 |
    45 | To: 46 | 47 |
    48 |
    49 |
    50 | 51 | 52 |
    53 |
    54 |
    55 | 56 | 57 |
    58 | We are good 59 |
    60 | 61 | 62 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 92 | 93 | -------------------------------------------------------------------------------- /other-demo-files/socket.io_site/form.cfm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 15 | 16 | 17 |

    Message Post Form

    18 |
    19 |
    20 | 21 |
    22 |
    23 | 24 |
    25 |
    26 |
    27 | 28 | 31 |
    32 |
    33 |
    34 |
    35 | 36 |
    37 |
    38 | 39 |
    40 |
    41 | 42 | 45 |
    46 | 47 | 48 | 49 | 50 | 82 | 83 | -------------------------------------------------------------------------------- /html-to-coldfusion/cfwebsocket.js: -------------------------------------------------------------------------------- 1 | function cfwebsocket ( 2 | appName, 3 | channel, 4 | appStartPath, 5 | onMessage, 6 | onSubscribe, 7 | isProxy, 8 | wsPort, 9 | server, 10 | secure, 11 | customOptions 12 | ){ 13 | var me = this; 14 | var theCustomOptions = customOptions || {}; 15 | 16 | this.server = server ? server : location.hostname; 17 | this.isSecure = secure ? secure : (location.protocol === 'https:' ? true : false); 18 | this.protocol = this.isSecure ? 'wss' : 'ws'; 19 | this.port = wsPort ? wsPort : (isProxy ? '' : (this.isSecure ? 8543 : 8577)); 20 | this.path = isProxy ? '/cfws/' : '/cfusion/cfusion'; 21 | this.server = `${this.protocol}://${this.server}${this.port ? `:${this.port}` : ''}`; 22 | this.ws = new WebSocket(`${this.server}${this.path}`); 23 | 24 | // cf json properties for sending messages 25 | this.subscribe = {ns: 'coldfusion.websocket.channels', type: 'subscribe', channel: channel, appName: appName,customOptions:theCustomOptions}, 26 | this.unsubscribe = {ns: 'coldfusion.websocket.channels', type: 'unsubscribe', channel: channel, appName: appName}, 27 | this.message = {ns: 'coldfusion.websocket.channels', type: 'publish', channel: channel, data:'', appName: appName,customOptions:theCustomOptions}; 28 | 29 | // WebSocket API Events 30 | this.ws.onopen = function(e){ 31 | me.subscribeMe(); 32 | }; 33 | 34 | this.ws.onmessage = function(e){ 35 | // when we receive a message pass it to the parseMessage function (the data piece of the object received) 36 | var data = JSON.parse(e.data); 37 | // looks like we are calling before CF has been executed 38 | if (data.code === -1 && data.msg.indexOf('is not running') !== -1){ 39 | if (appStartPath){ 40 | fetch(appStartPath,{ 41 | method : 'GET' 42 | }) 43 | .then( response => response.json() ) 44 | .then( success => { 45 | if (success) 46 | me.subscribeMe(); 47 | }); 48 | } 49 | } 50 | else if (data.code === 0 && data.type === 'response' && data.reqType === 'subscribe' && data.msg ==='ok' && onSubscribe && typeof onSubscribe === 'function'){ 51 | onSubscribe(); 52 | } 53 | // pass to our onMessage function we passed in 54 | if (onMessage && typeof onMessage === 'function'){ 55 | onMessage(data); 56 | } 57 | }; 58 | 59 | this.ws.onerror = function(e){ 60 | // console.log('onerror',e); 61 | }; 62 | 63 | this.ws.onclose = function(e){ 64 | // console.log('close',e); 65 | }; 66 | 67 | this.subscribeMe = function(){ 68 | if (this.ws.readyState === this.ws.OPEN) 69 | this.ws.send(JSON.stringify(this.subscribe)); 70 | } 71 | 72 | this.unsubscribeMe = function(){ 73 | if (this.ws.readyState === this.ws.OPEN) 74 | this.ws.send(JSON.stringify(this.unsubscribe)); 75 | } 76 | 77 | this.publish = function(value,customOptions){ 78 | var theCustomOptions = customOptions || {}; 79 | var obj = Object.assign({},this.message), 80 | publish = value !== ''; 81 | if (publish){ 82 | obj.data = value; 83 | // add custom options 84 | for (let key of Object.keys(theCustomOptions)) 85 | obj.customOptions[key] = theCustomOptions[key]; 86 | // send message 87 | this.ws.send(JSON.stringify(obj)); 88 | } 89 | return publish; 90 | } 91 | } -------------------------------------------------------------------------------- /other-demo-files/socket.io/server.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { createServer } from 'node:http'; 3 | import { fileURLToPath } from 'node:url'; 4 | import { dirname, join } from 'node:path'; 5 | import { Server } from 'socket.io'; 6 | 7 | const app = express(); 8 | const server = createServer(app); 9 | const io = new Server(server); 10 | 11 | const __dirname = dirname(fileURLToPath(import.meta.url)); 12 | 13 | app.use( express.static('public') ); 14 | app.use( '/publish', express.json() ); 15 | 16 | // The Simple WebSocket Server 17 | io.on('connection', socket => { 18 | 19 | const channels = socket.handshake.query.channels ? 20 | socket.handshake.query.channels.toString().split(',') : 21 | []; 22 | 23 | console.log( `user connected - ${socket.id}`, channels ); 24 | 25 | const subscribeTo = channels => { 26 | channels.forEach( channel => { 27 | socket.join( channel ); 28 | console.log( `subscribing ${socket.id} to channel ${channel}`); 29 | }); 30 | } 31 | 32 | const unsubscribeFrom = channels => { 33 | channels.forEach( channel => { 34 | socket.leave( channel ); 35 | console.log( `unsubscribing ${socket.id} from channel ${channel}`); 36 | }); 37 | } 38 | 39 | // join each channel passed (room) 40 | subscribeTo( channels ); 41 | 42 | socket.on('disconnect', () => { 43 | console.log( `user disconnected - ${socket.id}` ); 44 | }); 45 | 46 | socket.on('subscribe', channels => { 47 | channels = channels ? channels.toString().split(',') : []; 48 | subscribeTo( channels ); 49 | }); 50 | 51 | socket.on('unsubscribe', channels => { 52 | channels = channels ? channels.toString().split(',') : []; 53 | unsubscribeFrom( channels ); 54 | }); 55 | 56 | socket.on('publish', data => { 57 | console.log( 'publish ', data ); 58 | 59 | if ( data.constructor !== Object ) { 60 | data = { 61 | message : data 62 | }; 63 | } 64 | 65 | // send back the publisherId 66 | data.publisherId = socket.id; 67 | 68 | // emit the message to the other subscribers 69 | // use socket.broadcast to send to all except the sender 70 | if ( data.channel ) 71 | io.to( data.channel ).emit( 'message', data ); 72 | else 73 | io.emit( 'message', data ); 74 | }); 75 | }); 76 | 77 | // publish via post 78 | app.post('/publish', ( req, res ) => { 79 | console.log( `[200] ${req.method} ${req.url} via ${req.get('user-agent')} ${req.get('host')} ${req.ip}`); 80 | 81 | try { 82 | if ( !req.body.message ) { 83 | return res.status(400).json({'error':'message is required'}); 84 | } 85 | 86 | req.body.publisherId = 0; 87 | 88 | // if post to channel or global 89 | if ( req.body.channel ) 90 | io.to( req.body.channel ).emit( 'message', req.body ); 91 | else 92 | io.emit( 'message',req.body ); 93 | 94 | res.json( { 'success':true } ); 95 | } 96 | catch(e){ 97 | res.json( { 'success':false } ); 98 | } 99 | }); 100 | 101 | // get channel subscriber count and meta 102 | app.get('/subscribers/:channel', async ( req, res ) => { 103 | const sockets = ( await io.in( req.params.channel ).fetchSockets() ).map( socket => { 104 | return { 105 | id : socket.id , 106 | date : socket.handshake.time 107 | }; 108 | }); 109 | res.json( { 'success':true, 'count':sockets.length, sockets } ); 110 | }); 111 | 112 | server.listen( 3000, () => { 113 | console.log('server running at http://localhost:3000'); 114 | }); -------------------------------------------------------------------------------- /angular-to-coldfusion/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "websockets": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/websockets", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "src/tsconfig.app.json", 21 | "assets": [ 22 | "src/favicon.ico", 23 | "src/assets" 24 | ], 25 | "styles": [ 26 | "src/styles.css" 27 | ], 28 | "scripts": [] 29 | }, 30 | "configurations": { 31 | "production": { 32 | "fileReplacements": [ 33 | { 34 | "replace": "src/environments/environment.ts", 35 | "with": "src/environments/environment.prod.ts" 36 | } 37 | ], 38 | "optimization": true, 39 | "outputHashing": "all", 40 | "sourceMap": false, 41 | "extractCss": true, 42 | "namedChunks": false, 43 | "aot": true, 44 | "extractLicenses": true, 45 | "vendorChunk": false, 46 | "buildOptimizer": true 47 | } 48 | } 49 | }, 50 | "serve": { 51 | "builder": "@angular-devkit/build-angular:dev-server", 52 | "options": { 53 | "browserTarget": "websockets:build" 54 | }, 55 | "configurations": { 56 | "production": { 57 | "browserTarget": "websockets:build:production" 58 | } 59 | } 60 | }, 61 | "extract-i18n": { 62 | "builder": "@angular-devkit/build-angular:extract-i18n", 63 | "options": { 64 | "browserTarget": "websockets:build" 65 | } 66 | }, 67 | "test": { 68 | "builder": "@angular-devkit/build-angular:karma", 69 | "options": { 70 | "main": "src/test.ts", 71 | "polyfills": "src/polyfills.ts", 72 | "tsConfig": "src/tsconfig.spec.json", 73 | "karmaConfig": "src/karma.conf.js", 74 | "styles": [ 75 | "src/styles.css" 76 | ], 77 | "scripts": [], 78 | "assets": [ 79 | "src/favicon.ico", 80 | "src/assets" 81 | ] 82 | } 83 | }, 84 | "lint": { 85 | "builder": "@angular-devkit/build-angular:tslint", 86 | "options": { 87 | "tsConfig": [ 88 | "src/tsconfig.app.json", 89 | "src/tsconfig.spec.json" 90 | ], 91 | "exclude": [ 92 | "**/node_modules/**" 93 | ] 94 | } 95 | } 96 | } 97 | }, 98 | "websockets-e2e": { 99 | "root": "e2e/", 100 | "projectType": "application", 101 | "architect": { 102 | "e2e": { 103 | "builder": "@angular-devkit/build-angular:protractor", 104 | "options": { 105 | "protractorConfig": "e2e/protractor.conf.js", 106 | "devServerTarget": "websockets:serve" 107 | }, 108 | "configurations": { 109 | "production": { 110 | "devServerTarget": "websockets:serve:production" 111 | } 112 | } 113 | }, 114 | "lint": { 115 | "builder": "@angular-devkit/build-angular:tslint", 116 | "options": { 117 | "tsConfig": "e2e/tsconfig.e2e.json", 118 | "exclude": [ 119 | "**/node_modules/**" 120 | ] 121 | } 122 | } 123 | } 124 | } 125 | }, 126 | "defaultProject": "websockets" 127 | } -------------------------------------------------------------------------------- /chat/ChatListener.cfc: -------------------------------------------------------------------------------- 1 | component extends="CFIDE.websocket.ChannelListener" { 2 | 3 | public boolean function allowSubscribe( struct subscriberInfo ) { 4 | //I am crucial. You must use me. Or else. 5 | if( !arguments.subscriberInfo.connectionInfo.authenticated ) return false; 6 | //I'm an optional check just to be extra secure 7 | if( !structKeyExists(arguments.subscriberInfo,"username") ) return false; 8 | // send to all connected clients that this new subscriber has joined 9 | wsPublish( "chat",arguments.subscriberInfo.username & " has joined the chat room, say hello ... " ); 10 | // in a thread send to all clients (including just connected the full connected list) 11 | thread name="subscribers_#createUUID()#" action="run"{ 12 | returnConnectedUsers(); 13 | } 14 | return true; 15 | } 16 | 17 | public boolean function allowPublish( struct publisherInfo ) { 18 | return arguments.publisherInfo.connectionInfo.authenticated; 19 | } 20 | 21 | /** 22 | * Transforms the message into a struct that is used by beforeSendMessage 23 | * The struct containst the publisher's connection info and a to parameter 24 | * sent by the publisher when sending a private message 25 | * ( review /assets/chat.js to see how it is done ) 26 | */ 27 | public any function beforePublish( any message, struct publisherInfo ){ 28 | cflock( scope="application", timeout="10", type="exclusive" ){ 29 | application.publishedMessages++; 30 | } 31 | var messageData = { 32 | message : arguments.message, 33 | publisherInfo : arguments.publisherInfo.connectionInfo, 34 | to : arguments.publisherInfo.to ?: {} 35 | }; 36 | return messageData; 37 | } 38 | 39 | public any function beforeSendMessage( any message, struct subscriberInfo ){ 40 | var className = "message"; 41 | var isMe = false; 42 | var isPrivateMessage = false; 43 | var messageText = ""; 44 | var privateMessageText = ""; 45 | 46 | if ( isStruct(arguments.message.message) ){ 47 | messageText = arguments.message.message; 48 | } 49 | else if ( arguments.message.message == "FORCE-RECONNECT" ){ 50 | messageText = arguments.message.message; 51 | } 52 | else if ( structKeyExists(arguments.message.publisherInfo,"username") ){ 53 | isMe = !compare(arguments.subscriberInfo.connectioninfo.clientid,arguments.message.publisherInfo.clientid); 54 | 55 | isPrivateMessage = !structIsEmpty( arguments.message.to ?: {} ) && 56 | structKeyExists(arguments.message.to,"id") && 57 | structKeyExists(arguments.message.to,"username"); 58 | 59 | className &= ( isMe ? " me" : "" ) & 60 | ( isPrivateMessage ? " private" : "" ); 61 | 62 | if ( isPrivateMessage ){ 63 | privateMessageText = "Private 64 | #( isMe ? "to " & arguments.message.to.username : "from " & arguments.message.publisherInfo.username )# 65 | "; 66 | } 67 | messageText = "
    68 |
    69 | #arguments.message.publisherInfo.username# 70 | #privateMessageText# 71 |
    72 |
    #arguments.message.message#
    73 |
    "; 74 | } 75 | else { 76 | messageText = "
    77 |
    SYSTEM
    78 |
    #arguments.message.message#
    79 |
    "; 80 | } 81 | return messageText; 82 | } 83 | 84 | public any function canSendMessage(any message, struct subscriberInfo, struct publisherInfo) { 85 | var canSend = true; 86 | var privateToData = {}; 87 | 88 | // system messages skip this check as they are meant for all 89 | // which is done by making sure that publisherInfo.connectionInfo.clientID 90 | // exists as the system message does not have one 91 | if ( !isNull(arguments.publisherInfo.connectionInfo.clientID) ){ 92 | privateToData = !isNull(message.to) && isStruct(message.to) ? message.to : {}; 93 | // if not private 94 | // or myself 95 | // or message is for me 96 | canSend = ( structIsEmpty( privateToData ) ) || 97 | arguments.publisherInfo.connectionInfo.clientID == arguments.subscriberInfo.connectionInfo.clientID || 98 | privateToData.id == arguments.subscriberInfo.connectionInfo.clientID ; 99 | } 100 | return canSend; 101 | } 102 | 103 | public function afterUnsubscribe( struct subscriberInfo ){ 104 | wsPublish("chat",arguments.subscriberInfo.username & " has left the chat room"); 105 | returnConnectedUsers(); 106 | } 107 | 108 | /** 109 | * Publishes array of connected users for UI processing on client 110 | */ 111 | private function returnConnectedUsers(){ 112 | var _clients = wsGetSubscribers("chat"); 113 | var _client = {}; 114 | var clients = []; 115 | 116 | // sort the array 117 | arraySort(_clients,function (e1, e2){ 118 | return compare(e1.subscriberInfo.username, e2.subscriberInfo.username); 119 | }); 120 | 121 | for( _client in _clients ){ 122 | clients.append({ 123 | "id" : _client.clientid, 124 | "username" : _client.subscriberInfo.username, 125 | "city" : trim( _client.subscriberInfo.clientInfo.city ?: "" ), 126 | "region" : trim( _client.subscriberInfo.clientInfo.region ?: "" ) 127 | }); 128 | } 129 | 130 | wsPublish( "chat",{"type":"clients","data":clients} ); 131 | } 132 | } -------------------------------------------------------------------------------- /other-demo-files/socket.io_site/socketio.client.js: -------------------------------------------------------------------------------- 1 | var channels = ['demo','global'], 2 | socket = {}, 3 | server = {}, 4 | ws = {CONNECTED:0,NOTIFY:0}, 5 | _console = document.getElementById('console'), 6 | _clearlog = document.getElementById('clearlog'), 7 | _scroll = navigator.userAgent.indexOf('Firefox') !== -1, 8 | // message controls 9 | openMessage = document.getElementById('domessage'), 10 | openMessage = document.getElementById('domessage'), 11 | sendMessage = document.getElementById('sendmessage'), 12 | messageForm = document.getElementById('message'), 13 | messageText = document.getElementById('messagetext'); 14 | 15 | // let's fetch our info 16 | fetch('./info/',{type:'GET'}) 17 | .then( response => response.json() ) 18 | .then( response => { 19 | if(!response.success) 20 | window.alert(response.message); 21 | else 22 | callSocketIO(response.data.socketio_server,response.data.socketio_port); 23 | }); 24 | 25 | 26 | function callSocketIO(server,port){ 27 | // go an connect 28 | try{ 29 | socket = io(`${server}:${port}/`,{ 30 | query : 'channels=' + channels.join(',') 31 | }); 32 | setupSocketIO(); 33 | } 34 | catch(e){ 35 | window.alert('We could not connect to the socket IO instance, make sure you have started it.'); 36 | } 37 | } 38 | 39 | /* ========================================================================== 40 | SOCKET EVENTS 41 | ========================================================================== */ 42 | function setupSocketIO(){ 43 | socket.on('connect',function(){ 44 | ws.CONNECTED = 1; 45 | parseMessage('connected to ' + channels.join(',')); 46 | }); 47 | 48 | socket.on('disconnect',function(data){ 49 | ws.CONNECTED = 0; 50 | // set timeout to avoid opening alert if we refresh 51 | window.setTimeout(notifyDisconnect, 500); 52 | }); 53 | 54 | socket.on('reconnect',function(data){ 55 | ws.CONNECTED = 1; 56 | ws.NOTIFY = 0; 57 | parseMessage('We are back on'); 58 | }); 59 | 60 | socket.on('message',function(data){ 61 | parseMessage(data); 62 | // global reload 63 | if (data.message && data.message === 'RELOAD'){ 64 | parseMessage('I will reload in 3 seconds'); 65 | window.setTimeout(function(){ 66 | window.location.reload() 67 | },3000); 68 | } 69 | }); 70 | } 71 | 72 | /* ========================================================================== 73 | GLOBAL LISTENERS AND FUNCTIONS 74 | ========================================================================== */ 75 | _clearlog.addEventListener("click",function(e){ 76 | e.preventDefault(); 77 | _console.innerHTML = ''; 78 | }); 79 | 80 | function send(channel,message){ 81 | parseMessage(message); 82 | socket.emit('message',{channel : channel, message :message}); 83 | } 84 | 85 | function notifyDisconnect(){ 86 | if (ws.CONNECTED === 0 && ws.NOTIFY === 0){ 87 | parseMessage('We have been disconnected'); 88 | ws.NOTIFY++; 89 | } 90 | } 91 | 92 | function parseMessage(message){ 93 | var _date = new Date(), 94 | // format timestamp to a readable format 95 | _ts = _date.toLocaleDateString() + ' ' + _date.toLocaleTimeString(), 96 | // format message object into multiline readable string 97 | _data = JSON.stringify(message).replace(/,"/g,',\n "').replace('{','{\n ').replace('}','\n}'), 98 | // create new element 99 | _li = document.createElement('li'); 100 | // set the text 101 | _li.innerHTML = `${_ts}
    ${_data}
    `; 102 | // append element to console 103 | _console.appendChild(_li); 104 | 105 | // extra 106 | // if firefox use internal smooth functionality of scrollIntoView - if not animate using function 107 | if (document.body.scrollIntoView) 108 | document.body.scrollIntoView({block: "end", behavior: "smooth"}); 109 | } 110 | 111 | // Event Listeners on Buttons and Textarea 112 | openMessage.addEventListener('click',doToggleMessageForm); 113 | sendMessage.addEventListener('click',doSendMessage); 114 | messageText.addEventListener('keyup',function(event){ 115 | if(event.keyCode === 13 && event.shiftKey === true) 116 | doSendMessage(); 117 | }); 118 | 119 | // Toggle our message modal 120 | function doToggleMessageForm(){ 121 | if ( messageForm.classList.contains('hide') === true){ 122 | messageForm.classList.remove('hide'); 123 | messageForm.message.value = ''; 124 | } else { 125 | messageForm.classList.add('hide'); 126 | } 127 | } 128 | 129 | // Send a message to the server => publish() 130 | function doSendMessage(){ 131 | var theMessage = messageText.value.trim(); 132 | if (theMessage !== ''){ 133 | send('demo',theMessage); 134 | doToggleMessageForm(); 135 | } 136 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Realtime with WebSockets - Demo Code 2 | __Presented By [Giancarlo Gomez](https://github.com/GiancarloGomez)__ 3 | 4 | This is the demo code used in my WebSockets presentation. 5 | 6 | * __angular-to-coldfusion__
    The Angular Example I gave at CFSUMMIT2018. View README in directory on how to run. 7 | * __assets__
    The CSS and JavaScript files that are shared by the live demo UIs 8 | * __css__ 9 | * __js__ 10 | * __chat__
    The Chat demo 11 | * __html-to-coldfusion__
    The HTML to ColdFusion WebSocket example. 12 | * __live-demos__ 13 | * __demo-01__
    Minimal requirements 14 | * __demo-02__
    AdvancedSocket Example 15 | * __other-demo-files__ 16 | * __socket.io__ ( [node.js required](https://nodejs.org/) )
    17 | This is the code for the [socket.io](http://socket.io/) server that will run on port 8080. 18 | Remember to do an ``npm install`` after downloading and then run ``node server.js`` to start. 19 | * __socket.io_site__
    20 | The example site using the socket.io server to connect, send and receive messages both from the client and the server. 21 | This should be mapped to its own site or ran as a separate server using CommandBox. 22 | * __swift-to-coldfusion__
    The native iOS app example shown in CFSUMMIT2018. Getting this running is a bit more cumbersome and requires working with the [Starscream](https://github.com/daltoniam/Starscream) libray for using WebSockets in Swift. The code here includes the Main Storyboard and the main View Controller for reference. 23 | 24 | ## Running Demos ( CommandBox, Socket.IO and ) 25 | You can easily run the demos using CommandBox. Simply clone this repo to your local machine 26 | and execute the ``box start`` command at the ``root`` folder for the main app and in the ``other-demo-files\socket.io_site`` 27 | folder for the demo of using Socket.IO instead of ColdFusion's WebSockets. 28 | 29 | __The default server.json is set to use ColdFusion 2021. If you use this one make sure 30 | to install the websocket package using cfpm and/or thru the administrator. If you choose to 31 | do it from the CLI simply type the following while in CommandBox and/or the Server's bin folder__ 32 | 33 | ```bash 34 | cfpm install websocket 35 | ``` 36 | 37 | > Lucee server is not supported for these examples. If using Lucee, I recommend looking at the socket.io example 38 | or using an existing service for WebSocket communication. 39 | 40 | If you want to run other version's other than 2021 you can do so by starting the server as follows: 41 | ```bash 42 | # ColdFusion 2018 43 | box server start serverConfigFile=server.2018.json 44 | # ColdFusion 2016 45 | box server start serverConfigFile=server.2016.json 46 | # ColdFusion 11 ( make sure to point to supported JDK if system version not supported ) 47 | box server start serverConfigFile=server.11.json 48 | ``` 49 | 50 | __Application.cfc Settings__ 51 | To run this code you must set 2 request variables in ``Application.cfc``, these variables are for the site ran from ``other-demo-files\socket.io_site`` 52 | 53 | ```javascript 54 | request.socketio_server = "http://127.0.0.1:51095/"; 55 | request.socketio_broadcast = "http://127.0.0.1:51095/broadcast/"; 56 | ``` 57 | 58 | __Socket.IO__
    59 | As stated above you will need to run ``node server.js`` command at the ``other-demo-files\socket.io`` folder. 60 | 61 | __HTML To ColdFusion Demo__
    62 | Once you have configured both of your servers, make sure to go into the ``html-to-coldfusion\script.js`` and set 63 | the values to the WebSocket Port used by ColdFusion and the Path and Port to your server. 64 | 65 | ```javascript 66 | var cfws = new cfwebsocket( 67 | // appName for the server we are interacting => live-demos\demo-01 68 | 'websockets_demo1', 69 | // the name of the channel we are subscribing to 70 | 'demo', 71 | // the file to run in case a ColdFusion request has not execute yet 72 | 'http://localhost:50320/live-demos/demo-01/runme/', 73 | // the global function to execute to parse a message received 74 | parseMessage, 75 | // the global function to execute when we subscribe 76 | subscribed, 77 | // if we are using the proxy method 78 | false, 79 | // the Port used by ColdFusion for WebSockets ( if not included it uses defaults defined in cfwebsocket.js) 80 | 8581, 81 | // the server we are connecting to 82 | 'localhost', 83 | // if the connection should be secure 84 | false 85 | ); 86 | ``` 87 | 88 | __Angular To ColdFusion Demo__
    89 | Same as above, simply open the `angular-to-coldfusion\src\app\app.component.ts` file and set the attributes based on your ColdFusion instance. 90 | ```javascript 91 | this.cfwebsocket = new window['cfwebsocket']( 92 | // appName for the server we are interacting => live-demos\demo-01 93 | 'websockets_demo1', 94 | // the name of the channel we are subscribing to 95 | 'demo', 96 | // the file to run in case a ColdFusion request has not execute yet 97 | 'http://localhost:50320/live-demos/demo-01/runme/', 98 | // the global function to execute to parse a message received 99 | // we pass entire object and allow defaults to pass back to correct function 100 | this, 101 | // the global function to execute when we subscribe 102 | // we pass entire object and allow defaults to pass back to correct function 103 | this, 104 | // if we are using the proxy method 105 | false, 106 | // the Port used by ColdFusion for WebSockets ( if not included it uses defaults defined in cfwebsocket.js) 107 | 8581, 108 | // the server we are connecting to 109 | 'localhost', 110 | // if the connection should be secure 111 | false 112 | ) 113 | ``` 114 | 115 | ## Conferences 116 | * NCDevCon 2016 117 | * Dev.Objective 2016 118 | * ColdFusion Summit 2016 119 | * ColdFusion Summit 2017 120 | * ColdFusion Summit 2018 121 | 122 | ## Slides 123 | All slides can be found in the [ColdFusion Realtime With WebSockets Repo](https://github.com/GiancarloGomez/ColdFusion-Realtime-With-WebSockets "ColdFusion Realtime With WebSockets Repo") 124 | -------------------------------------------------------------------------------- /swift-to-coldfusion/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /index.cfm: -------------------------------------------------------------------------------- 1 | 2 | if( !structKeyExists(request,"socketio_server") ) 3 | throw("You must set the full URL to the Socket.IO Console Site to request.socketio_server - review instructions in Application.onRequestStart()"); 4 | if( !structKeyExists(request,"socketio_broadcast") ) 5 | throw("You must set the full URL to the Socket.IO Broadcast Handler to request.socketio_broadcast - review instructions in Application.onRequestStart()"); 6 | links = [ 7 | { 8 | title : "DEMOS", 9 | description : "", 10 | nav : [ 11 | {link:"/live-demos/demo-00/",title:"Minimal"}, 12 | {link:"/live-demos/demo-00/restricted/",title:"Restricted"}, 13 | {link:"/live-demos/demo-01/",title:"Simple"}, 14 | {link:"/live-demos/demo-01/advanced/",title:"Advanced"}, 15 | {link:"/live-demos/broadcast/",title:"Broadcast",ajax:true}, 16 | {link:"/live-demos/subscribers/",title:"Subscribers",modal:true}, 17 | {link:"/live-demos/console/",title:"Console"} 18 | ] 19 | }, 20 | // { 21 | // title : "DEMO 2", 22 | // description : "ADVANCED", 23 | // nav : [ 24 | // {link:"/live-demos/demo-02/",title:"Simple Console"}, 25 | // {link:"/live-demos/demo-02/advanced/",title:"Advanced Console"}, 26 | // {link:"/live-demos/demo-02/broadcast/",title:"Broadcast",ajax:true}, 27 | // {link:"/live-demos/demo-02/subscribers/",title:"Subscribers",modal:true} 28 | // ] 29 | // }, 30 | { 31 | title : "CHAT", 32 | description : "", 33 | nav : [ 34 | {link:"/chat/",title:"Chat App"}, 35 | {link:"/chat/broadcast/",title:"Broadcast",ajax:true}, 36 | {link:"/chat/subscribers/",title:"Subscribers",modal:true}, 37 | {link:"/chat/console/",title:"Console"} 38 | ] 39 | } 40 | // { 41 | // title : "OTHER", 42 | // description : "", 43 | // nav : [ 44 | // {link:"/html-to-coldfusion/",title:"HTML to ColdFusion"}, 45 | // {link:request.socketio_server,title:"Socket.IO Console"}, 46 | // {link:request.socketio_broadcast,title:"Socket.IO Broadcast",ajax:true} 47 | // ] 48 | // } 49 | ]; 50 | 51 | 52 | 53 | 54 | 55 | 56 | WebSockets 201 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
    66 |

    67 | WebSockets 201 68 |

    69 | 70 | 71 |
    72 | #link.title# 73 |
    74 | 75 | #nav.title# 76 | 77 |
    78 |
    79 |
    80 |
    81 |
    82 | 83 |

    84 | Giancarlo Gomez
    85 | Fuse Developments • 86 | CrossTrackr 87 |
    88 |   89 |   90 |   91 |   92 |   93 |   94 | 95 |

    96 | 97 | 108 | 109 | 110 | 111 | 112 | 174 | 175 | -------------------------------------------------------------------------------- /chat/assets/chat.js: -------------------------------------------------------------------------------- 1 | const Chat = { 2 | connected: false, 3 | clients: [], 4 | channel: null, 5 | username: null, 6 | clientID: null, 7 | ui: { 8 | body: document.body, 9 | loginModal: null, 10 | usernameField: null, 11 | usernameErrorText: null, 12 | usernameFormGroup: null, 13 | clearMessages: null, 14 | leaveRoom: null, 15 | messageToField: null, 16 | messageField: null, 17 | messageForm: null, 18 | messages: null, 19 | users: null 20 | 21 | }, 22 | init : function() { 23 | const _this = this, 24 | _ui = this.ui; 25 | // setup modal and show 26 | _ui.loginModal = $('#login') 27 | .modal({ 28 | backdrop : 'static', 29 | keyboard : false 30 | }) 31 | .modal('show'); 32 | // setup rest of UI 33 | _ui.usernameField = _ui.loginModal.find('input'); 34 | _ui.usernameErrorText = _ui.loginModal.find('.invalid-feedback'); 35 | _ui.usernameFormGroup = _ui.usernameField.parents('.input-group'); 36 | _ui.messageForm = $('#messageFrm'); 37 | _ui.messageToField = $('#message-to'); 38 | _ui.messageField = $('#message'); 39 | _ui.leaveRoom = $('#leave-room'); 40 | _ui.clearMessages = document.getElementById('clear-messages') 41 | _ui.messages = document.getElementById('messages'); 42 | _ui.users = document.getElementById('users'); 43 | 44 | // listeners 45 | 46 | // focus on login field 47 | _ui.loginModal.on('shown.bs.modal',function(e){ 48 | _ui.usernameField.val('').focus(); 49 | _this.clearMessages(); 50 | _ui.users.innerHTML = ''; 51 | }); 52 | 53 | // username blur 54 | _ui.usernameField.on('focus',function(){ 55 | _ui.usernameField.removeClass('is-invalid'); 56 | _ui.usernameErrorText.html(''); 57 | }); 58 | 59 | // clear our log 60 | _ui.clearMessages.addEventListener('click',_this.clearMessages.bind(_this)); 61 | 62 | // join room 63 | _ui.loginModal.find('#loginFrm').submit(_this.login.bind(_this)); 64 | 65 | // leave room 66 | _ui.leaveRoom.on('click',_this.logout.bind(_this)); 67 | 68 | // send message 69 | _ui.messageForm.submit(_this.sendMessage.bind(_this)); 70 | }, 71 | clearMessages: function( event ){ 72 | if ( event ) 73 | event.preventDefault(); 74 | this.ui.messages.innerHTML = ''; 75 | }, 76 | login: function ( event ){ 77 | if ( event ){ 78 | event.preventDefault(); 79 | let username = this.ui.usernameField.val().trim(); 80 | if ( username === '' ){ 81 | this.ui.usernameField.addClass('is-invalid'); 82 | this.ui.usernameErrorText.html('Please enter a username to login'); 83 | } 84 | else { 85 | // set the username into our Client Info 86 | AdvancedSocket.clientInfo.username = username; 87 | // Authenticate User 88 | // On Success => AdvancedSocket.connected 89 | // On Fail => AdvancedSocket.onError 90 | ws.authenticate( username, '' ); 91 | } 92 | } 93 | }, 94 | loginError: function(){ 95 | this.ui.usernameField.addClass('is-invalid').val(''); 96 | this.ui.usernameErrorText.html('Invalid User, please try again'); 97 | }, 98 | logout: function( event ){ 99 | if ( event ){ 100 | event.preventDefault(); 101 | // Unsubscribe 102 | ws.unsubscribe( this.channel ); 103 | // data updates 104 | this.connected = false; 105 | this.clientID = 106 | this.channel = 107 | this.username = null; 108 | // UI Updates 109 | this.ui.body.classList.add('off'); 110 | this.ui.loginModal.modal('show'); 111 | // change our AdvancedSocket to not run the Auto Connect feature 112 | AdvancedSocket.autoConnect = false; 113 | AdvancedSocket.checkConnection(); 114 | } 115 | }, 116 | receiveMessage: function( obj ){ 117 | if( typeof obj.data === 'object' ){ 118 | // process connected user menu 119 | this.clients = obj.data.data; 120 | this.updateClients(); 121 | } 122 | else if ( obj.data !== 'FORCE-RECONNECT' ) { 123 | this.ui.messages.innerHTML += obj.data; 124 | // animate the scroll 125 | if ( typeof document.body.scrollIntoView === 'function' ){ 126 | this.ui.messages.querySelector('.message:last-child').scrollIntoView({ 127 | behavior : 'smooth' 128 | }); 129 | } 130 | else { 131 | $(this.ui.messages).animate({scrollTop : this.ui.messages.scrollHeight + 'px'},500); 132 | } 133 | } 134 | }, 135 | sendMessage:function( event ){ 136 | if ( event ){ 137 | event.preventDefault(); 138 | 139 | let value = this.ui.messageField.val().trim(), 140 | toID = this.ui.messageToField.val(), 141 | data = {}; 142 | 143 | if ( value !== '' ){ 144 | // for private message 145 | if ( toID !== '' ) 146 | data.to = this.clients.find(client => client.id === toID ); 147 | // publish 148 | ws.publish( this.channel, value, data ); 149 | // reset message field 150 | this.ui.messageField.val('').focus(); 151 | } 152 | } 153 | }, 154 | updateClients: function() { 155 | let userList = '
      ', 156 | options = ''; 157 | this.clients.forEach(client => { 158 | // do not include me in the message to options 159 | if ( client.id !== this.clientID ) 160 | options += ``; 161 | userList += `
    • 162 | ${client.username} 163 | ${(client.city + client.region).length ? `${client.city}, ${client.region}` : ''} 164 |
    • `; 165 | }); 166 | this.ui.users.innerHTML = userList; 167 | this.ui.messageToField.html( options ) 168 | }, 169 | welcome: function( clientID, username, channel ){ 170 | if ( clientID && username ){ 171 | this.ui.messages.innerHTML += `
      172 |
      Welcome to the chat room ${username}
      173 |
      `; 174 | // UI Updates 175 | this.ui.usernameField.val(''); 176 | this.ui.body.classList.remove('off'); 177 | this.ui.loginModal.modal('hide'); 178 | this.ui.messageField.focus(); 179 | this.ui.leaveRoom.removeClass('hide'); 180 | // data update 181 | this.connected = true; 182 | this.clientID = clientID; 183 | this.channel = channel; 184 | this.username = username; 185 | } 186 | } 187 | }; 188 | 189 | // bind global to Chat receive message function 190 | // for use with AdvanceSocket until I update to work with 191 | // namespace functions 192 | var receiveMessage = Chat.receiveMessage.bind(Chat); 193 | 194 | // AdvanceSocket connect and onError overrides 195 | AdvancedSocket.connected = function (){ 196 | // initial connect 197 | if ( Chat.connected === false ) 198 | Chat.welcome( AdvancedSocket.clientID, AdvancedSocket.clientInfo.username, AdvancedSocket.channels[0] ); 199 | // set timer and status element 200 | AdvancedSocket.timerCount = AdvancedSocket.onlineTimer; 201 | AdvancedSocket.statusLabel.className = 'alert alert-success text-center'; 202 | AdvancedSocket.statusLabel.innerHTML = 'We are connected!!!'; 203 | } 204 | 205 | AdvancedSocket.onError = function(obj){ 206 | // when an error occurs in the websocket 207 | if ( obj.reqType === 'authenticate' && obj.code === -1 ){ 208 | Chat.loginError(); 209 | AdvancedSocket.clientInfo.username = ''; 210 | } 211 | // AdvancedSocket.log('onError',obj); 212 | }; 213 | 214 | document.addEventListener('DOMContentLoaded',() => { 215 | Chat.init(); 216 | }, false); -------------------------------------------------------------------------------- /live-demos/html/cfwebsocket.js: -------------------------------------------------------------------------------- 1 | class CFWebSocket { 2 | 3 | constructor({ 4 | appName, 5 | isProxy = false, 6 | appStartUrl, 7 | wsUrl, 8 | channel, 9 | subscriberInfo, 10 | onMessage, 11 | onSubscribe, 12 | onError, 13 | onClose 14 | } = Object()){ 15 | this.doLog('initialized'); 16 | if ( !appStartUrl ) 17 | throw( 'appStartUrl must be passed in the options' ); 18 | if ( !wsUrl ) 19 | throw( 'wsUrl must be passed in the options' ); 20 | const isSecure = location.protocol === 'https:' ? true : false; 21 | // set options 22 | this.options = { 23 | appName : appName, 24 | // path to use for ws connection 25 | appPath : isProxy ? '/cfws' : '/cfusion/cfusion', 26 | // set full server url to run on open to make sure 27 | // ColdFusion Application has been started 28 | appStartUrl : `//${appStartUrl}`, 29 | // set full server url to connect to 30 | wsUrl : `${isSecure ? 'wss' : 'ws'}://` + 31 | wsUrl + 32 | ( isProxy ? '/cfws' : '/cfusion/cfusion' ), 33 | // initial channels and subsriberInfo 34 | channel : channel, 35 | subscriberInfo : subscriberInfo, 36 | // For WebSocket API Events 37 | onMessage : onMessage, 38 | onSubscribe : onSubscribe, 39 | onError : onError, 40 | onClose : onClose, 41 | NS : 'coldfusion.websocket.channels' 42 | }; 43 | // define WebSocket connection 44 | this.ws = null; 45 | this.channels = []; 46 | this.openConnection(); 47 | // create event listeners 48 | window.addEventListener('offline', evt => { 49 | this.doLog( 'I am offline!!!' ); 50 | // AdvancedSocket.setTimer(false); 51 | // AdvancedSocket.disconnected(); 52 | // if we go fully offline kill any pending timer 53 | // clearTimeout(AdvancedSocket.timer); 54 | // AdvancedSocket.doLog('AdvancedSocket : Event','offline',e); 55 | }, false); 56 | window.addEventListener('online', evt => { 57 | this.doLog( 'I am online!!!' ); 58 | this.closeConnection(); 59 | this.openConnection(); 60 | // AdvancedSocket.doLog('AdvancedSocket : Event','online',e); 61 | // restart connection check 62 | // AdvancedSocket.checkConnection(); 63 | }, false); 64 | } 65 | 66 | openConnection(){ 67 | this.doLog('openConnection'); 68 | // Create connection to the WebSocket server 69 | this.ws = new WebSocket( this.options.wsUrl ); 70 | // for tracking user initiated connection close requests 71 | this.ws.userInitiatedClose = false; 72 | // WebSocket API Events 73 | this.ws.onopen = async (e) => { 74 | // to make sure we fired off a cfm file to start the 75 | // application run the following 76 | const firstCall = await fetch( this.options.appStartUrl ); 77 | const channels = this.channels.length ? this.channels : this.options.channel; 78 | if ( firstCall.status === 200 && channels) 79 | this.subscribe( channels ); 80 | return; 81 | }; 82 | this.ws.onmessage = (e) => { 83 | this.doLog('onmessage'); 84 | // parse the message received 85 | var data = JSON.parse(e.data); 86 | // pass to our onSubscribe callback 87 | if ( data.code === 0 && data.type === 'response' && data.reqType === 'subscribe' && data.msg ==='ok' && typeof this.options.onSubscribe === 'function' ) 88 | this.options.onSubscribe( data ); 89 | // pass to our onMessage callback 90 | if ( typeof this.options.onMessage === 'function' ) 91 | this.options.onMessage(data); 92 | // if it is a force reconnect ( from an application reload ) 93 | if ( data.type === 'data' && data.data === 'FORCE-RECONNECT' ){ 94 | this.openConnection(); 95 | } 96 | }; 97 | this.ws.onerror = (e) => { 98 | this.doLog('onerror'); 99 | if ( typeof this.options.onError === 'function' ) 100 | this.options.onError(e); 101 | else 102 | console.error(e); 103 | }; 104 | this.ws.onclose = (e) => { 105 | this.doLog('onclose', this.ws); 106 | // this means we went offline we need to reconnect 107 | if ( this.ws !== null ) 108 | this.openConnection(); 109 | }; 110 | } 111 | 112 | closeConnection( userInitiatedClose ){ 113 | this.doLog('closeConnection'); 114 | if ( this.isConnected() ){ 115 | this.ws.userInitiatedClose = userInitiatedClose || true; 116 | this.ws.close(); 117 | if ( typeof this.options.onClose === 'function' ) 118 | this.options.onClose( this.options ); 119 | this.ws = null; 120 | } 121 | } 122 | 123 | authenticate( username, password ){ 124 | this.doLog('authenticate'); 125 | const isConnected = this.isConnected(); 126 | if ( isConnected ){ 127 | this.sendToken({ 128 | type : 'authenticate', 129 | username : username, 130 | password : password 131 | }); 132 | } 133 | return isConnected; 134 | } 135 | 136 | subscribe( channel, subscriberInfo ){ 137 | this.doLog('subscribe',{ channel, subscriberInfo }); 138 | const isConnected = this.isConnected(); 139 | // allow multiple channel subcribe 140 | if ( isConnected ){ 141 | const _channels = Array.isArray(channel) ? channel : channel.split(','); 142 | _channels.forEach( _channel => { 143 | this.channels.push( _channel ); 144 | this.sendToken({ 145 | type : 'subscribe', 146 | channel : _channel, 147 | customOptions : this.validateCustomOptions( subscriberInfo || this.options.subscriberInfo ) 148 | }); 149 | }); 150 | } 151 | return isConnected; 152 | } 153 | 154 | unsubscribe( channel ){ 155 | this.doLog('unsubscribe'); 156 | const isConnected = this.isConnected(); 157 | // allow multiple channel unsubscribe 158 | if ( isConnected ){ 159 | const _channels = Array.isArray(channel) ? channel : channel.split(','); 160 | _channels.forEach( _channel => { 161 | const _index = this.channels.indexOf( _channel ); 162 | if ( _index !== -1 ) 163 | this.channels.splice( _index,1 ); 164 | this.sendToken({ 165 | type : 'unsubscribe', 166 | channel : _channel 167 | }); 168 | }); 169 | } 170 | return isConnected; 171 | } 172 | 173 | publish( channel, message, customOptions ){ 174 | this.doLog('publish'); 175 | const isConnected = this.isConnected(); 176 | if ( isConnected ){ 177 | this.sendToken({ 178 | type : 'publish', 179 | channel : channel, 180 | data : message, 181 | customOptions : this.validateCustomOptions( customOptions ) 182 | }); 183 | } 184 | return isConnected; 185 | } 186 | 187 | invoke( cfcName, cfcMethod, methodArguments ){ 188 | this.doLog('invoke'); 189 | const isConnected = this.isConnected(); 190 | if ( isConnected ){ 191 | this.sendToken({ 192 | type : 'invoke', 193 | cfcName : cfcName, 194 | cfcMethod : cfcMethod, 195 | methodArguments : this.validateMethodArguments( methodArguments ), 196 | referrer : location.pathname 197 | }); 198 | } 199 | return isConnected; 200 | } 201 | 202 | invokeAndPublish( channel, cfcName, cfcMethod, methodArguments, customOptions ){ 203 | this.doLog('invokeAndPublish'); 204 | const isConnected = this.isConnected(); 205 | if ( isConnected ){ 206 | this.sendToken({ 207 | type : 'invokeAndPublish', 208 | channel : channel, 209 | cfcName : cfcName, 210 | cfcMethod : cfcMethod, 211 | methodArguments : this.validateMethodArguments( methodArguments ), 212 | referrer : location.pathname, 213 | customOptions : this.validateCustomOptions( customOptions ) 214 | }); 215 | } 216 | return isConnected; 217 | } 218 | 219 | getSubscriberCount( channel ){ 220 | this.doLog('invokeAndPublish'); 221 | if ( !this.isConnected() ) 222 | return; 223 | 224 | return this.sendToken({ 225 | type : 'getSubscriberCount', 226 | channel : channel 227 | }); 228 | } 229 | 230 | getSubscriptions(){ 231 | this.doLog('getSubscriptions'); 232 | if ( !this.isConnected() ) 233 | return; 234 | return this.sendToken({ 235 | type : 'getSubscriptions' 236 | }); 237 | } 238 | 239 | isConnected(){ 240 | this.doLog('isConnected'); 241 | return this.ws instanceof WebSocket && 242 | this.ws.readyState === this.ws.OPEN; 243 | } 244 | 245 | sendToken( token ){ 246 | this.doLog('sendToken'); 247 | if ( token.customOptions && Object.keys(token.customOptions).length === 0 ) 248 | delete token.customOptions; 249 | this.ws.send( JSON.stringify({ 250 | ns : this.options.NS, 251 | appName : this.options.appName, 252 | ...token 253 | })); 254 | } 255 | 256 | validateCustomOptions( customOptions ){ 257 | this.doLog('validateCustomOptions'); 258 | return typeof customOptions == 'object' ? customOptions : {}; 259 | } 260 | 261 | validateMethodArguments( methodArguments ){ 262 | this.doLog('validateMethodArguments'); 263 | return Array.isArray( methodArguments ) ? methodArguments : [] 264 | } 265 | } 266 | 267 | // for debugging purposes 268 | CFWebSocket.prototype.doLog = new URLSearchParams(location.search).get('debug') !== null && window.console ? console.log.bind(window.console) : () => {} -------------------------------------------------------------------------------- /other-demo-files/socket.io/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample-socket-io", 3 | "version": "0.0.1", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "sample-socket-io", 9 | "version": "0.0.1", 10 | "dependencies": { 11 | "express": "^4.19.2", 12 | "socket.io": "^4.7.5" 13 | } 14 | }, 15 | "node_modules/@socket.io/component-emitter": { 16 | "version": "3.1.2", 17 | "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", 18 | "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" 19 | }, 20 | "node_modules/@types/cookie": { 21 | "version": "0.4.1", 22 | "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", 23 | "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" 24 | }, 25 | "node_modules/@types/cors": { 26 | "version": "2.8.17", 27 | "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", 28 | "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", 29 | "dependencies": { 30 | "@types/node": "*" 31 | } 32 | }, 33 | "node_modules/@types/node": { 34 | "version": "20.14.10", 35 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", 36 | "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", 37 | "dependencies": { 38 | "undici-types": "~5.26.4" 39 | } 40 | }, 41 | "node_modules/accepts": { 42 | "version": "1.3.8", 43 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 44 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 45 | "dependencies": { 46 | "mime-types": "~2.1.34", 47 | "negotiator": "0.6.3" 48 | }, 49 | "engines": { 50 | "node": ">= 0.6" 51 | } 52 | }, 53 | "node_modules/array-flatten": { 54 | "version": "1.1.1", 55 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 56 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 57 | }, 58 | "node_modules/base64id": { 59 | "version": "2.0.0", 60 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", 61 | "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", 62 | "engines": { 63 | "node": "^4.5.0 || >= 5.9" 64 | } 65 | }, 66 | "node_modules/body-parser": { 67 | "version": "1.20.2", 68 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", 69 | "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", 70 | "dependencies": { 71 | "bytes": "3.1.2", 72 | "content-type": "~1.0.5", 73 | "debug": "2.6.9", 74 | "depd": "2.0.0", 75 | "destroy": "1.2.0", 76 | "http-errors": "2.0.0", 77 | "iconv-lite": "0.4.24", 78 | "on-finished": "2.4.1", 79 | "qs": "6.11.0", 80 | "raw-body": "2.5.2", 81 | "type-is": "~1.6.18", 82 | "unpipe": "1.0.0" 83 | }, 84 | "engines": { 85 | "node": ">= 0.8", 86 | "npm": "1.2.8000 || >= 1.4.16" 87 | } 88 | }, 89 | "node_modules/bytes": { 90 | "version": "3.1.2", 91 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 92 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 93 | "engines": { 94 | "node": ">= 0.8" 95 | } 96 | }, 97 | "node_modules/call-bind": { 98 | "version": "1.0.7", 99 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", 100 | "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", 101 | "dependencies": { 102 | "es-define-property": "^1.0.0", 103 | "es-errors": "^1.3.0", 104 | "function-bind": "^1.1.2", 105 | "get-intrinsic": "^1.2.4", 106 | "set-function-length": "^1.2.1" 107 | }, 108 | "engines": { 109 | "node": ">= 0.4" 110 | }, 111 | "funding": { 112 | "url": "https://github.com/sponsors/ljharb" 113 | } 114 | }, 115 | "node_modules/content-disposition": { 116 | "version": "0.5.4", 117 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 118 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 119 | "dependencies": { 120 | "safe-buffer": "5.2.1" 121 | }, 122 | "engines": { 123 | "node": ">= 0.6" 124 | } 125 | }, 126 | "node_modules/content-disposition/node_modules/safe-buffer": { 127 | "version": "5.2.1", 128 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 129 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 130 | "funding": [ 131 | { 132 | "type": "github", 133 | "url": "https://github.com/sponsors/feross" 134 | }, 135 | { 136 | "type": "patreon", 137 | "url": "https://www.patreon.com/feross" 138 | }, 139 | { 140 | "type": "consulting", 141 | "url": "https://feross.org/support" 142 | } 143 | ] 144 | }, 145 | "node_modules/content-type": { 146 | "version": "1.0.5", 147 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 148 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 149 | "engines": { 150 | "node": ">= 0.6" 151 | } 152 | }, 153 | "node_modules/cookie": { 154 | "version": "0.4.2", 155 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", 156 | "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", 157 | "engines": { 158 | "node": ">= 0.6" 159 | } 160 | }, 161 | "node_modules/cookie-signature": { 162 | "version": "1.0.6", 163 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 164 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 165 | }, 166 | "node_modules/cors": { 167 | "version": "2.8.5", 168 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 169 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 170 | "dependencies": { 171 | "object-assign": "^4", 172 | "vary": "^1" 173 | }, 174 | "engines": { 175 | "node": ">= 0.10" 176 | } 177 | }, 178 | "node_modules/debug": { 179 | "version": "2.6.9", 180 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 181 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 182 | "dependencies": { 183 | "ms": "2.0.0" 184 | } 185 | }, 186 | "node_modules/define-data-property": { 187 | "version": "1.1.4", 188 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", 189 | "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 190 | "dependencies": { 191 | "es-define-property": "^1.0.0", 192 | "es-errors": "^1.3.0", 193 | "gopd": "^1.0.1" 194 | }, 195 | "engines": { 196 | "node": ">= 0.4" 197 | }, 198 | "funding": { 199 | "url": "https://github.com/sponsors/ljharb" 200 | } 201 | }, 202 | "node_modules/depd": { 203 | "version": "2.0.0", 204 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 205 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 206 | "engines": { 207 | "node": ">= 0.8" 208 | } 209 | }, 210 | "node_modules/destroy": { 211 | "version": "1.2.0", 212 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 213 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", 214 | "engines": { 215 | "node": ">= 0.8", 216 | "npm": "1.2.8000 || >= 1.4.16" 217 | } 218 | }, 219 | "node_modules/ee-first": { 220 | "version": "1.1.1", 221 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 222 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 223 | }, 224 | "node_modules/encodeurl": { 225 | "version": "1.0.2", 226 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 227 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", 228 | "engines": { 229 | "node": ">= 0.8" 230 | } 231 | }, 232 | "node_modules/engine.io": { 233 | "version": "6.5.5", 234 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", 235 | "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", 236 | "dependencies": { 237 | "@types/cookie": "^0.4.1", 238 | "@types/cors": "^2.8.12", 239 | "@types/node": ">=10.0.0", 240 | "accepts": "~1.3.4", 241 | "base64id": "2.0.0", 242 | "cookie": "~0.4.1", 243 | "cors": "~2.8.5", 244 | "debug": "~4.3.1", 245 | "engine.io-parser": "~5.2.1", 246 | "ws": "~8.17.1" 247 | }, 248 | "engines": { 249 | "node": ">=10.2.0" 250 | } 251 | }, 252 | "node_modules/engine.io-parser": { 253 | "version": "5.2.2", 254 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", 255 | "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", 256 | "engines": { 257 | "node": ">=10.0.0" 258 | } 259 | }, 260 | "node_modules/engine.io/node_modules/debug": { 261 | "version": "4.3.5", 262 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 263 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 264 | "dependencies": { 265 | "ms": "2.1.2" 266 | }, 267 | "engines": { 268 | "node": ">=6.0" 269 | }, 270 | "peerDependenciesMeta": { 271 | "supports-color": { 272 | "optional": true 273 | } 274 | } 275 | }, 276 | "node_modules/engine.io/node_modules/ms": { 277 | "version": "2.1.2", 278 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 279 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 280 | }, 281 | "node_modules/es-define-property": { 282 | "version": "1.0.0", 283 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", 284 | "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", 285 | "dependencies": { 286 | "get-intrinsic": "^1.2.4" 287 | }, 288 | "engines": { 289 | "node": ">= 0.4" 290 | } 291 | }, 292 | "node_modules/es-errors": { 293 | "version": "1.3.0", 294 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 295 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 296 | "engines": { 297 | "node": ">= 0.4" 298 | } 299 | }, 300 | "node_modules/escape-html": { 301 | "version": "1.0.3", 302 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 303 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 304 | }, 305 | "node_modules/etag": { 306 | "version": "1.8.1", 307 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 308 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 309 | "engines": { 310 | "node": ">= 0.6" 311 | } 312 | }, 313 | "node_modules/express": { 314 | "version": "4.19.2", 315 | "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", 316 | "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", 317 | "dependencies": { 318 | "accepts": "~1.3.8", 319 | "array-flatten": "1.1.1", 320 | "body-parser": "1.20.2", 321 | "content-disposition": "0.5.4", 322 | "content-type": "~1.0.4", 323 | "cookie": "0.6.0", 324 | "cookie-signature": "1.0.6", 325 | "debug": "2.6.9", 326 | "depd": "2.0.0", 327 | "encodeurl": "~1.0.2", 328 | "escape-html": "~1.0.3", 329 | "etag": "~1.8.1", 330 | "finalhandler": "1.2.0", 331 | "fresh": "0.5.2", 332 | "http-errors": "2.0.0", 333 | "merge-descriptors": "1.0.1", 334 | "methods": "~1.1.2", 335 | "on-finished": "2.4.1", 336 | "parseurl": "~1.3.3", 337 | "path-to-regexp": "0.1.7", 338 | "proxy-addr": "~2.0.7", 339 | "qs": "6.11.0", 340 | "range-parser": "~1.2.1", 341 | "safe-buffer": "5.2.1", 342 | "send": "0.18.0", 343 | "serve-static": "1.15.0", 344 | "setprototypeof": "1.2.0", 345 | "statuses": "2.0.1", 346 | "type-is": "~1.6.18", 347 | "utils-merge": "1.0.1", 348 | "vary": "~1.1.2" 349 | }, 350 | "engines": { 351 | "node": ">= 0.10.0" 352 | } 353 | }, 354 | "node_modules/express/node_modules/cookie": { 355 | "version": "0.6.0", 356 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", 357 | "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", 358 | "engines": { 359 | "node": ">= 0.6" 360 | } 361 | }, 362 | "node_modules/express/node_modules/safe-buffer": { 363 | "version": "5.2.1", 364 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 365 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 366 | "funding": [ 367 | { 368 | "type": "github", 369 | "url": "https://github.com/sponsors/feross" 370 | }, 371 | { 372 | "type": "patreon", 373 | "url": "https://www.patreon.com/feross" 374 | }, 375 | { 376 | "type": "consulting", 377 | "url": "https://feross.org/support" 378 | } 379 | ] 380 | }, 381 | "node_modules/finalhandler": { 382 | "version": "1.2.0", 383 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", 384 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", 385 | "dependencies": { 386 | "debug": "2.6.9", 387 | "encodeurl": "~1.0.2", 388 | "escape-html": "~1.0.3", 389 | "on-finished": "2.4.1", 390 | "parseurl": "~1.3.3", 391 | "statuses": "2.0.1", 392 | "unpipe": "~1.0.0" 393 | }, 394 | "engines": { 395 | "node": ">= 0.8" 396 | } 397 | }, 398 | "node_modules/forwarded": { 399 | "version": "0.2.0", 400 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 401 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 402 | "engines": { 403 | "node": ">= 0.6" 404 | } 405 | }, 406 | "node_modules/fresh": { 407 | "version": "0.5.2", 408 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 409 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", 410 | "engines": { 411 | "node": ">= 0.6" 412 | } 413 | }, 414 | "node_modules/function-bind": { 415 | "version": "1.1.2", 416 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 417 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 418 | "funding": { 419 | "url": "https://github.com/sponsors/ljharb" 420 | } 421 | }, 422 | "node_modules/get-intrinsic": { 423 | "version": "1.2.4", 424 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", 425 | "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", 426 | "dependencies": { 427 | "es-errors": "^1.3.0", 428 | "function-bind": "^1.1.2", 429 | "has-proto": "^1.0.1", 430 | "has-symbols": "^1.0.3", 431 | "hasown": "^2.0.0" 432 | }, 433 | "engines": { 434 | "node": ">= 0.4" 435 | }, 436 | "funding": { 437 | "url": "https://github.com/sponsors/ljharb" 438 | } 439 | }, 440 | "node_modules/gopd": { 441 | "version": "1.0.1", 442 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", 443 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", 444 | "dependencies": { 445 | "get-intrinsic": "^1.1.3" 446 | }, 447 | "funding": { 448 | "url": "https://github.com/sponsors/ljharb" 449 | } 450 | }, 451 | "node_modules/has-property-descriptors": { 452 | "version": "1.0.2", 453 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 454 | "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 455 | "dependencies": { 456 | "es-define-property": "^1.0.0" 457 | }, 458 | "funding": { 459 | "url": "https://github.com/sponsors/ljharb" 460 | } 461 | }, 462 | "node_modules/has-proto": { 463 | "version": "1.0.3", 464 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", 465 | "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", 466 | "engines": { 467 | "node": ">= 0.4" 468 | }, 469 | "funding": { 470 | "url": "https://github.com/sponsors/ljharb" 471 | } 472 | }, 473 | "node_modules/has-symbols": { 474 | "version": "1.0.3", 475 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 476 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 477 | "engines": { 478 | "node": ">= 0.4" 479 | }, 480 | "funding": { 481 | "url": "https://github.com/sponsors/ljharb" 482 | } 483 | }, 484 | "node_modules/hasown": { 485 | "version": "2.0.2", 486 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 487 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 488 | "dependencies": { 489 | "function-bind": "^1.1.2" 490 | }, 491 | "engines": { 492 | "node": ">= 0.4" 493 | } 494 | }, 495 | "node_modules/http-errors": { 496 | "version": "2.0.0", 497 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 498 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 499 | "dependencies": { 500 | "depd": "2.0.0", 501 | "inherits": "2.0.4", 502 | "setprototypeof": "1.2.0", 503 | "statuses": "2.0.1", 504 | "toidentifier": "1.0.1" 505 | }, 506 | "engines": { 507 | "node": ">= 0.8" 508 | } 509 | }, 510 | "node_modules/iconv-lite": { 511 | "version": "0.4.24", 512 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 513 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 514 | "dependencies": { 515 | "safer-buffer": ">= 2.1.2 < 3" 516 | }, 517 | "engines": { 518 | "node": ">=0.10.0" 519 | } 520 | }, 521 | "node_modules/inherits": { 522 | "version": "2.0.4", 523 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 524 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 525 | }, 526 | "node_modules/ipaddr.js": { 527 | "version": "1.9.1", 528 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 529 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 530 | "engines": { 531 | "node": ">= 0.10" 532 | } 533 | }, 534 | "node_modules/media-typer": { 535 | "version": "0.3.0", 536 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 537 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 538 | "engines": { 539 | "node": ">= 0.6" 540 | } 541 | }, 542 | "node_modules/merge-descriptors": { 543 | "version": "1.0.1", 544 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 545 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 546 | }, 547 | "node_modules/methods": { 548 | "version": "1.1.2", 549 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 550 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", 551 | "engines": { 552 | "node": ">= 0.6" 553 | } 554 | }, 555 | "node_modules/mime": { 556 | "version": "1.6.0", 557 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 558 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 559 | "bin": { 560 | "mime": "cli.js" 561 | }, 562 | "engines": { 563 | "node": ">=4" 564 | } 565 | }, 566 | "node_modules/mime-db": { 567 | "version": "1.52.0", 568 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 569 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 570 | "engines": { 571 | "node": ">= 0.6" 572 | } 573 | }, 574 | "node_modules/mime-types": { 575 | "version": "2.1.35", 576 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 577 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 578 | "dependencies": { 579 | "mime-db": "1.52.0" 580 | }, 581 | "engines": { 582 | "node": ">= 0.6" 583 | } 584 | }, 585 | "node_modules/ms": { 586 | "version": "2.0.0", 587 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 588 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 589 | }, 590 | "node_modules/negotiator": { 591 | "version": "0.6.3", 592 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 593 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 594 | "engines": { 595 | "node": ">= 0.6" 596 | } 597 | }, 598 | "node_modules/object-assign": { 599 | "version": "4.1.1", 600 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 601 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 602 | "engines": { 603 | "node": ">=0.10.0" 604 | } 605 | }, 606 | "node_modules/object-inspect": { 607 | "version": "1.13.2", 608 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", 609 | "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", 610 | "engines": { 611 | "node": ">= 0.4" 612 | }, 613 | "funding": { 614 | "url": "https://github.com/sponsors/ljharb" 615 | } 616 | }, 617 | "node_modules/on-finished": { 618 | "version": "2.4.1", 619 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 620 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 621 | "dependencies": { 622 | "ee-first": "1.1.1" 623 | }, 624 | "engines": { 625 | "node": ">= 0.8" 626 | } 627 | }, 628 | "node_modules/parseurl": { 629 | "version": "1.3.3", 630 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 631 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 632 | "engines": { 633 | "node": ">= 0.8" 634 | } 635 | }, 636 | "node_modules/path-to-regexp": { 637 | "version": "0.1.7", 638 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 639 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 640 | }, 641 | "node_modules/proxy-addr": { 642 | "version": "2.0.7", 643 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 644 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 645 | "dependencies": { 646 | "forwarded": "0.2.0", 647 | "ipaddr.js": "1.9.1" 648 | }, 649 | "engines": { 650 | "node": ">= 0.10" 651 | } 652 | }, 653 | "node_modules/qs": { 654 | "version": "6.11.0", 655 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 656 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 657 | "dependencies": { 658 | "side-channel": "^1.0.4" 659 | }, 660 | "engines": { 661 | "node": ">=0.6" 662 | }, 663 | "funding": { 664 | "url": "https://github.com/sponsors/ljharb" 665 | } 666 | }, 667 | "node_modules/range-parser": { 668 | "version": "1.2.1", 669 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 670 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 671 | "engines": { 672 | "node": ">= 0.6" 673 | } 674 | }, 675 | "node_modules/raw-body": { 676 | "version": "2.5.2", 677 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", 678 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", 679 | "dependencies": { 680 | "bytes": "3.1.2", 681 | "http-errors": "2.0.0", 682 | "iconv-lite": "0.4.24", 683 | "unpipe": "1.0.0" 684 | }, 685 | "engines": { 686 | "node": ">= 0.8" 687 | } 688 | }, 689 | "node_modules/safer-buffer": { 690 | "version": "2.1.2", 691 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 692 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 693 | }, 694 | "node_modules/send": { 695 | "version": "0.18.0", 696 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", 697 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", 698 | "dependencies": { 699 | "debug": "2.6.9", 700 | "depd": "2.0.0", 701 | "destroy": "1.2.0", 702 | "encodeurl": "~1.0.2", 703 | "escape-html": "~1.0.3", 704 | "etag": "~1.8.1", 705 | "fresh": "0.5.2", 706 | "http-errors": "2.0.0", 707 | "mime": "1.6.0", 708 | "ms": "2.1.3", 709 | "on-finished": "2.4.1", 710 | "range-parser": "~1.2.1", 711 | "statuses": "2.0.1" 712 | }, 713 | "engines": { 714 | "node": ">= 0.8.0" 715 | } 716 | }, 717 | "node_modules/send/node_modules/ms": { 718 | "version": "2.1.3", 719 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 720 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 721 | }, 722 | "node_modules/serve-static": { 723 | "version": "1.15.0", 724 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", 725 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", 726 | "dependencies": { 727 | "encodeurl": "~1.0.2", 728 | "escape-html": "~1.0.3", 729 | "parseurl": "~1.3.3", 730 | "send": "0.18.0" 731 | }, 732 | "engines": { 733 | "node": ">= 0.8.0" 734 | } 735 | }, 736 | "node_modules/set-function-length": { 737 | "version": "1.2.2", 738 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", 739 | "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 740 | "dependencies": { 741 | "define-data-property": "^1.1.4", 742 | "es-errors": "^1.3.0", 743 | "function-bind": "^1.1.2", 744 | "get-intrinsic": "^1.2.4", 745 | "gopd": "^1.0.1", 746 | "has-property-descriptors": "^1.0.2" 747 | }, 748 | "engines": { 749 | "node": ">= 0.4" 750 | } 751 | }, 752 | "node_modules/setprototypeof": { 753 | "version": "1.2.0", 754 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 755 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 756 | }, 757 | "node_modules/side-channel": { 758 | "version": "1.0.6", 759 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", 760 | "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", 761 | "dependencies": { 762 | "call-bind": "^1.0.7", 763 | "es-errors": "^1.3.0", 764 | "get-intrinsic": "^1.2.4", 765 | "object-inspect": "^1.13.1" 766 | }, 767 | "engines": { 768 | "node": ">= 0.4" 769 | }, 770 | "funding": { 771 | "url": "https://github.com/sponsors/ljharb" 772 | } 773 | }, 774 | "node_modules/socket.io": { 775 | "version": "4.7.5", 776 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", 777 | "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", 778 | "dependencies": { 779 | "accepts": "~1.3.4", 780 | "base64id": "~2.0.0", 781 | "cors": "~2.8.5", 782 | "debug": "~4.3.2", 783 | "engine.io": "~6.5.2", 784 | "socket.io-adapter": "~2.5.2", 785 | "socket.io-parser": "~4.2.4" 786 | }, 787 | "engines": { 788 | "node": ">=10.2.0" 789 | } 790 | }, 791 | "node_modules/socket.io-adapter": { 792 | "version": "2.5.5", 793 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", 794 | "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", 795 | "dependencies": { 796 | "debug": "~4.3.4", 797 | "ws": "~8.17.1" 798 | } 799 | }, 800 | "node_modules/socket.io-adapter/node_modules/debug": { 801 | "version": "4.3.5", 802 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 803 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 804 | "dependencies": { 805 | "ms": "2.1.2" 806 | }, 807 | "engines": { 808 | "node": ">=6.0" 809 | }, 810 | "peerDependenciesMeta": { 811 | "supports-color": { 812 | "optional": true 813 | } 814 | } 815 | }, 816 | "node_modules/socket.io-adapter/node_modules/ms": { 817 | "version": "2.1.2", 818 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 819 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 820 | }, 821 | "node_modules/socket.io-parser": { 822 | "version": "4.2.4", 823 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", 824 | "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", 825 | "dependencies": { 826 | "@socket.io/component-emitter": "~3.1.0", 827 | "debug": "~4.3.1" 828 | }, 829 | "engines": { 830 | "node": ">=10.0.0" 831 | } 832 | }, 833 | "node_modules/socket.io-parser/node_modules/debug": { 834 | "version": "4.3.5", 835 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 836 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 837 | "dependencies": { 838 | "ms": "2.1.2" 839 | }, 840 | "engines": { 841 | "node": ">=6.0" 842 | }, 843 | "peerDependenciesMeta": { 844 | "supports-color": { 845 | "optional": true 846 | } 847 | } 848 | }, 849 | "node_modules/socket.io-parser/node_modules/ms": { 850 | "version": "2.1.2", 851 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 852 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 853 | }, 854 | "node_modules/socket.io/node_modules/debug": { 855 | "version": "4.3.5", 856 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 857 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 858 | "dependencies": { 859 | "ms": "2.1.2" 860 | }, 861 | "engines": { 862 | "node": ">=6.0" 863 | }, 864 | "peerDependenciesMeta": { 865 | "supports-color": { 866 | "optional": true 867 | } 868 | } 869 | }, 870 | "node_modules/socket.io/node_modules/ms": { 871 | "version": "2.1.2", 872 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 873 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 874 | }, 875 | "node_modules/statuses": { 876 | "version": "2.0.1", 877 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 878 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 879 | "engines": { 880 | "node": ">= 0.8" 881 | } 882 | }, 883 | "node_modules/toidentifier": { 884 | "version": "1.0.1", 885 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 886 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 887 | "engines": { 888 | "node": ">=0.6" 889 | } 890 | }, 891 | "node_modules/type-is": { 892 | "version": "1.6.18", 893 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 894 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 895 | "dependencies": { 896 | "media-typer": "0.3.0", 897 | "mime-types": "~2.1.24" 898 | }, 899 | "engines": { 900 | "node": ">= 0.6" 901 | } 902 | }, 903 | "node_modules/undici-types": { 904 | "version": "5.26.5", 905 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 906 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 907 | }, 908 | "node_modules/unpipe": { 909 | "version": "1.0.0", 910 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 911 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 912 | "engines": { 913 | "node": ">= 0.8" 914 | } 915 | }, 916 | "node_modules/utils-merge": { 917 | "version": "1.0.1", 918 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 919 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", 920 | "engines": { 921 | "node": ">= 0.4.0" 922 | } 923 | }, 924 | "node_modules/vary": { 925 | "version": "1.1.2", 926 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 927 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", 928 | "engines": { 929 | "node": ">= 0.8" 930 | } 931 | }, 932 | "node_modules/ws": { 933 | "version": "8.17.1", 934 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", 935 | "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", 936 | "engines": { 937 | "node": ">=10.0.0" 938 | }, 939 | "peerDependencies": { 940 | "bufferutil": "^4.0.1", 941 | "utf-8-validate": ">=5.0.2" 942 | }, 943 | "peerDependenciesMeta": { 944 | "bufferutil": { 945 | "optional": true 946 | }, 947 | "utf-8-validate": { 948 | "optional": true 949 | } 950 | } 951 | } 952 | }, 953 | "dependencies": { 954 | "@socket.io/component-emitter": { 955 | "version": "3.1.2", 956 | "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", 957 | "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" 958 | }, 959 | "@types/cookie": { 960 | "version": "0.4.1", 961 | "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", 962 | "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" 963 | }, 964 | "@types/cors": { 965 | "version": "2.8.17", 966 | "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", 967 | "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", 968 | "requires": { 969 | "@types/node": "*" 970 | } 971 | }, 972 | "@types/node": { 973 | "version": "20.14.10", 974 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", 975 | "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", 976 | "requires": { 977 | "undici-types": "~5.26.4" 978 | } 979 | }, 980 | "accepts": { 981 | "version": "1.3.8", 982 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 983 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 984 | "requires": { 985 | "mime-types": "~2.1.34", 986 | "negotiator": "0.6.3" 987 | } 988 | }, 989 | "array-flatten": { 990 | "version": "1.1.1", 991 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 992 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 993 | }, 994 | "base64id": { 995 | "version": "2.0.0", 996 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", 997 | "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" 998 | }, 999 | "body-parser": { 1000 | "version": "1.20.2", 1001 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", 1002 | "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", 1003 | "requires": { 1004 | "bytes": "3.1.2", 1005 | "content-type": "~1.0.5", 1006 | "debug": "2.6.9", 1007 | "depd": "2.0.0", 1008 | "destroy": "1.2.0", 1009 | "http-errors": "2.0.0", 1010 | "iconv-lite": "0.4.24", 1011 | "on-finished": "2.4.1", 1012 | "qs": "6.11.0", 1013 | "raw-body": "2.5.2", 1014 | "type-is": "~1.6.18", 1015 | "unpipe": "1.0.0" 1016 | } 1017 | }, 1018 | "bytes": { 1019 | "version": "3.1.2", 1020 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 1021 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" 1022 | }, 1023 | "call-bind": { 1024 | "version": "1.0.7", 1025 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", 1026 | "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", 1027 | "requires": { 1028 | "es-define-property": "^1.0.0", 1029 | "es-errors": "^1.3.0", 1030 | "function-bind": "^1.1.2", 1031 | "get-intrinsic": "^1.2.4", 1032 | "set-function-length": "^1.2.1" 1033 | } 1034 | }, 1035 | "content-disposition": { 1036 | "version": "0.5.4", 1037 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 1038 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 1039 | "requires": { 1040 | "safe-buffer": "5.2.1" 1041 | }, 1042 | "dependencies": { 1043 | "safe-buffer": { 1044 | "version": "5.2.1", 1045 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1046 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 1047 | } 1048 | } 1049 | }, 1050 | "content-type": { 1051 | "version": "1.0.5", 1052 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 1053 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" 1054 | }, 1055 | "cookie": { 1056 | "version": "0.4.2", 1057 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", 1058 | "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" 1059 | }, 1060 | "cookie-signature": { 1061 | "version": "1.0.6", 1062 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 1063 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 1064 | }, 1065 | "cors": { 1066 | "version": "2.8.5", 1067 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 1068 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 1069 | "requires": { 1070 | "object-assign": "^4", 1071 | "vary": "^1" 1072 | } 1073 | }, 1074 | "debug": { 1075 | "version": "2.6.9", 1076 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1077 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1078 | "requires": { 1079 | "ms": "2.0.0" 1080 | } 1081 | }, 1082 | "define-data-property": { 1083 | "version": "1.1.4", 1084 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", 1085 | "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 1086 | "requires": { 1087 | "es-define-property": "^1.0.0", 1088 | "es-errors": "^1.3.0", 1089 | "gopd": "^1.0.1" 1090 | } 1091 | }, 1092 | "depd": { 1093 | "version": "2.0.0", 1094 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 1095 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 1096 | }, 1097 | "destroy": { 1098 | "version": "1.2.0", 1099 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 1100 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" 1101 | }, 1102 | "ee-first": { 1103 | "version": "1.1.1", 1104 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 1105 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 1106 | }, 1107 | "encodeurl": { 1108 | "version": "1.0.2", 1109 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 1110 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" 1111 | }, 1112 | "engine.io": { 1113 | "version": "6.5.5", 1114 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", 1115 | "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", 1116 | "requires": { 1117 | "@types/cookie": "^0.4.1", 1118 | "@types/cors": "^2.8.12", 1119 | "@types/node": ">=10.0.0", 1120 | "accepts": "~1.3.4", 1121 | "base64id": "2.0.0", 1122 | "cookie": "~0.4.1", 1123 | "cors": "~2.8.5", 1124 | "debug": "~4.3.1", 1125 | "engine.io-parser": "~5.2.1", 1126 | "ws": "~8.17.1" 1127 | }, 1128 | "dependencies": { 1129 | "debug": { 1130 | "version": "4.3.5", 1131 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 1132 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 1133 | "requires": { 1134 | "ms": "2.1.2" 1135 | } 1136 | }, 1137 | "ms": { 1138 | "version": "2.1.2", 1139 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1140 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1141 | } 1142 | } 1143 | }, 1144 | "engine.io-parser": { 1145 | "version": "5.2.2", 1146 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", 1147 | "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==" 1148 | }, 1149 | "es-define-property": { 1150 | "version": "1.0.0", 1151 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", 1152 | "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", 1153 | "requires": { 1154 | "get-intrinsic": "^1.2.4" 1155 | } 1156 | }, 1157 | "es-errors": { 1158 | "version": "1.3.0", 1159 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 1160 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" 1161 | }, 1162 | "escape-html": { 1163 | "version": "1.0.3", 1164 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 1165 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 1166 | }, 1167 | "etag": { 1168 | "version": "1.8.1", 1169 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 1170 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" 1171 | }, 1172 | "express": { 1173 | "version": "4.19.2", 1174 | "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", 1175 | "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", 1176 | "requires": { 1177 | "accepts": "~1.3.8", 1178 | "array-flatten": "1.1.1", 1179 | "body-parser": "1.20.2", 1180 | "content-disposition": "0.5.4", 1181 | "content-type": "~1.0.4", 1182 | "cookie": "0.6.0", 1183 | "cookie-signature": "1.0.6", 1184 | "debug": "2.6.9", 1185 | "depd": "2.0.0", 1186 | "encodeurl": "~1.0.2", 1187 | "escape-html": "~1.0.3", 1188 | "etag": "~1.8.1", 1189 | "finalhandler": "1.2.0", 1190 | "fresh": "0.5.2", 1191 | "http-errors": "2.0.0", 1192 | "merge-descriptors": "1.0.1", 1193 | "methods": "~1.1.2", 1194 | "on-finished": "2.4.1", 1195 | "parseurl": "~1.3.3", 1196 | "path-to-regexp": "0.1.7", 1197 | "proxy-addr": "~2.0.7", 1198 | "qs": "6.11.0", 1199 | "range-parser": "~1.2.1", 1200 | "safe-buffer": "5.2.1", 1201 | "send": "0.18.0", 1202 | "serve-static": "1.15.0", 1203 | "setprototypeof": "1.2.0", 1204 | "statuses": "2.0.1", 1205 | "type-is": "~1.6.18", 1206 | "utils-merge": "1.0.1", 1207 | "vary": "~1.1.2" 1208 | }, 1209 | "dependencies": { 1210 | "cookie": { 1211 | "version": "0.6.0", 1212 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", 1213 | "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" 1214 | }, 1215 | "safe-buffer": { 1216 | "version": "5.2.1", 1217 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1218 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 1219 | } 1220 | } 1221 | }, 1222 | "finalhandler": { 1223 | "version": "1.2.0", 1224 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", 1225 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", 1226 | "requires": { 1227 | "debug": "2.6.9", 1228 | "encodeurl": "~1.0.2", 1229 | "escape-html": "~1.0.3", 1230 | "on-finished": "2.4.1", 1231 | "parseurl": "~1.3.3", 1232 | "statuses": "2.0.1", 1233 | "unpipe": "~1.0.0" 1234 | } 1235 | }, 1236 | "forwarded": { 1237 | "version": "0.2.0", 1238 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 1239 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" 1240 | }, 1241 | "fresh": { 1242 | "version": "0.5.2", 1243 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 1244 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" 1245 | }, 1246 | "function-bind": { 1247 | "version": "1.1.2", 1248 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 1249 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" 1250 | }, 1251 | "get-intrinsic": { 1252 | "version": "1.2.4", 1253 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", 1254 | "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", 1255 | "requires": { 1256 | "es-errors": "^1.3.0", 1257 | "function-bind": "^1.1.2", 1258 | "has-proto": "^1.0.1", 1259 | "has-symbols": "^1.0.3", 1260 | "hasown": "^2.0.0" 1261 | } 1262 | }, 1263 | "gopd": { 1264 | "version": "1.0.1", 1265 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", 1266 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", 1267 | "requires": { 1268 | "get-intrinsic": "^1.1.3" 1269 | } 1270 | }, 1271 | "has-property-descriptors": { 1272 | "version": "1.0.2", 1273 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 1274 | "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 1275 | "requires": { 1276 | "es-define-property": "^1.0.0" 1277 | } 1278 | }, 1279 | "has-proto": { 1280 | "version": "1.0.3", 1281 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", 1282 | "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==" 1283 | }, 1284 | "has-symbols": { 1285 | "version": "1.0.3", 1286 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 1287 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" 1288 | }, 1289 | "hasown": { 1290 | "version": "2.0.2", 1291 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 1292 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 1293 | "requires": { 1294 | "function-bind": "^1.1.2" 1295 | } 1296 | }, 1297 | "http-errors": { 1298 | "version": "2.0.0", 1299 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 1300 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 1301 | "requires": { 1302 | "depd": "2.0.0", 1303 | "inherits": "2.0.4", 1304 | "setprototypeof": "1.2.0", 1305 | "statuses": "2.0.1", 1306 | "toidentifier": "1.0.1" 1307 | } 1308 | }, 1309 | "iconv-lite": { 1310 | "version": "0.4.24", 1311 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 1312 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 1313 | "requires": { 1314 | "safer-buffer": ">= 2.1.2 < 3" 1315 | } 1316 | }, 1317 | "inherits": { 1318 | "version": "2.0.4", 1319 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1320 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1321 | }, 1322 | "ipaddr.js": { 1323 | "version": "1.9.1", 1324 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 1325 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 1326 | }, 1327 | "media-typer": { 1328 | "version": "0.3.0", 1329 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1330 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" 1331 | }, 1332 | "merge-descriptors": { 1333 | "version": "1.0.1", 1334 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 1335 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 1336 | }, 1337 | "methods": { 1338 | "version": "1.1.2", 1339 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1340 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 1341 | }, 1342 | "mime": { 1343 | "version": "1.6.0", 1344 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1345 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 1346 | }, 1347 | "mime-db": { 1348 | "version": "1.52.0", 1349 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 1350 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" 1351 | }, 1352 | "mime-types": { 1353 | "version": "2.1.35", 1354 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 1355 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 1356 | "requires": { 1357 | "mime-db": "1.52.0" 1358 | } 1359 | }, 1360 | "ms": { 1361 | "version": "2.0.0", 1362 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1363 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1364 | }, 1365 | "negotiator": { 1366 | "version": "0.6.3", 1367 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 1368 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" 1369 | }, 1370 | "object-assign": { 1371 | "version": "4.1.1", 1372 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1373 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" 1374 | }, 1375 | "object-inspect": { 1376 | "version": "1.13.2", 1377 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", 1378 | "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==" 1379 | }, 1380 | "on-finished": { 1381 | "version": "2.4.1", 1382 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 1383 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 1384 | "requires": { 1385 | "ee-first": "1.1.1" 1386 | } 1387 | }, 1388 | "parseurl": { 1389 | "version": "1.3.3", 1390 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1391 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 1392 | }, 1393 | "path-to-regexp": { 1394 | "version": "0.1.7", 1395 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1396 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 1397 | }, 1398 | "proxy-addr": { 1399 | "version": "2.0.7", 1400 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 1401 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 1402 | "requires": { 1403 | "forwarded": "0.2.0", 1404 | "ipaddr.js": "1.9.1" 1405 | } 1406 | }, 1407 | "qs": { 1408 | "version": "6.11.0", 1409 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 1410 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 1411 | "requires": { 1412 | "side-channel": "^1.0.4" 1413 | } 1414 | }, 1415 | "range-parser": { 1416 | "version": "1.2.1", 1417 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1418 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 1419 | }, 1420 | "raw-body": { 1421 | "version": "2.5.2", 1422 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", 1423 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", 1424 | "requires": { 1425 | "bytes": "3.1.2", 1426 | "http-errors": "2.0.0", 1427 | "iconv-lite": "0.4.24", 1428 | "unpipe": "1.0.0" 1429 | } 1430 | }, 1431 | "safer-buffer": { 1432 | "version": "2.1.2", 1433 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1434 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1435 | }, 1436 | "send": { 1437 | "version": "0.18.0", 1438 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", 1439 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", 1440 | "requires": { 1441 | "debug": "2.6.9", 1442 | "depd": "2.0.0", 1443 | "destroy": "1.2.0", 1444 | "encodeurl": "~1.0.2", 1445 | "escape-html": "~1.0.3", 1446 | "etag": "~1.8.1", 1447 | "fresh": "0.5.2", 1448 | "http-errors": "2.0.0", 1449 | "mime": "1.6.0", 1450 | "ms": "2.1.3", 1451 | "on-finished": "2.4.1", 1452 | "range-parser": "~1.2.1", 1453 | "statuses": "2.0.1" 1454 | }, 1455 | "dependencies": { 1456 | "ms": { 1457 | "version": "2.1.3", 1458 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1459 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1460 | } 1461 | } 1462 | }, 1463 | "serve-static": { 1464 | "version": "1.15.0", 1465 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", 1466 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", 1467 | "requires": { 1468 | "encodeurl": "~1.0.2", 1469 | "escape-html": "~1.0.3", 1470 | "parseurl": "~1.3.3", 1471 | "send": "0.18.0" 1472 | } 1473 | }, 1474 | "set-function-length": { 1475 | "version": "1.2.2", 1476 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", 1477 | "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 1478 | "requires": { 1479 | "define-data-property": "^1.1.4", 1480 | "es-errors": "^1.3.0", 1481 | "function-bind": "^1.1.2", 1482 | "get-intrinsic": "^1.2.4", 1483 | "gopd": "^1.0.1", 1484 | "has-property-descriptors": "^1.0.2" 1485 | } 1486 | }, 1487 | "setprototypeof": { 1488 | "version": "1.2.0", 1489 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 1490 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 1491 | }, 1492 | "side-channel": { 1493 | "version": "1.0.6", 1494 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", 1495 | "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", 1496 | "requires": { 1497 | "call-bind": "^1.0.7", 1498 | "es-errors": "^1.3.0", 1499 | "get-intrinsic": "^1.2.4", 1500 | "object-inspect": "^1.13.1" 1501 | } 1502 | }, 1503 | "socket.io": { 1504 | "version": "4.7.5", 1505 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", 1506 | "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", 1507 | "requires": { 1508 | "accepts": "~1.3.4", 1509 | "base64id": "~2.0.0", 1510 | "cors": "~2.8.5", 1511 | "debug": "~4.3.2", 1512 | "engine.io": "~6.5.2", 1513 | "socket.io-adapter": "~2.5.2", 1514 | "socket.io-parser": "~4.2.4" 1515 | }, 1516 | "dependencies": { 1517 | "debug": { 1518 | "version": "4.3.5", 1519 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 1520 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 1521 | "requires": { 1522 | "ms": "2.1.2" 1523 | } 1524 | }, 1525 | "ms": { 1526 | "version": "2.1.2", 1527 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1528 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1529 | } 1530 | } 1531 | }, 1532 | "socket.io-adapter": { 1533 | "version": "2.5.5", 1534 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", 1535 | "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", 1536 | "requires": { 1537 | "debug": "~4.3.4", 1538 | "ws": "~8.17.1" 1539 | }, 1540 | "dependencies": { 1541 | "debug": { 1542 | "version": "4.3.5", 1543 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 1544 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 1545 | "requires": { 1546 | "ms": "2.1.2" 1547 | } 1548 | }, 1549 | "ms": { 1550 | "version": "2.1.2", 1551 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1552 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1553 | } 1554 | } 1555 | }, 1556 | "socket.io-parser": { 1557 | "version": "4.2.4", 1558 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", 1559 | "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", 1560 | "requires": { 1561 | "@socket.io/component-emitter": "~3.1.0", 1562 | "debug": "~4.3.1" 1563 | }, 1564 | "dependencies": { 1565 | "debug": { 1566 | "version": "4.3.5", 1567 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 1568 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 1569 | "requires": { 1570 | "ms": "2.1.2" 1571 | } 1572 | }, 1573 | "ms": { 1574 | "version": "2.1.2", 1575 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1576 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1577 | } 1578 | } 1579 | }, 1580 | "statuses": { 1581 | "version": "2.0.1", 1582 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 1583 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" 1584 | }, 1585 | "toidentifier": { 1586 | "version": "1.0.1", 1587 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 1588 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" 1589 | }, 1590 | "type-is": { 1591 | "version": "1.6.18", 1592 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 1593 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1594 | "requires": { 1595 | "media-typer": "0.3.0", 1596 | "mime-types": "~2.1.24" 1597 | } 1598 | }, 1599 | "undici-types": { 1600 | "version": "5.26.5", 1601 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 1602 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 1603 | }, 1604 | "unpipe": { 1605 | "version": "1.0.0", 1606 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1607 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" 1608 | }, 1609 | "utils-merge": { 1610 | "version": "1.0.1", 1611 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1612 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 1613 | }, 1614 | "vary": { 1615 | "version": "1.1.2", 1616 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1617 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 1618 | }, 1619 | "ws": { 1620 | "version": "8.17.1", 1621 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", 1622 | "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", 1623 | "requires": {} 1624 | } 1625 | } 1626 | } 1627 | --------------------------------------------------------------------------------