mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-04 15:08:01 +00:00
Feat/navigate (#81)
* feat/navigate upgrade react router * feat/navigate migrate to react router 6 * feat/navigate style modal * feat/navigate create endpoints for app settings * feat/navigate protect editor with pin * feat/navigate apply dynamic routing draft * feat/navigate upgrade relevant packages * feat/navigate restructure directory and add tests * feat/navigate test aliases validation * feat/navigate validate aliases before sending * feat/navigate create data endpoint * feat/navigate config: prettier * feat/navigate invalidate empty strings * feat/navigate create endpoints * feat/navigate parse on import * feat/navigate navigate to alias * feat/navigate user help and sample data * feat/navigate refact aliases modal * feat/navigate refact settings style * feat/navigate update sample db * feat/navigate link is relative to hostname * feat/navigate navigate to first match * feat/navigate update readme and version bump * feat/navigate config: create shared module * feat/navigate config: cheat module install * feat/navigate fix tests * feat/navigate run tests in pull request * Update ontime_cy.yml
This commit is contained in:
+9
-7
@@ -1,6 +1,7 @@
|
||||
// get environment vars
|
||||
import 'dotenv/config';
|
||||
import { sessionId, user } from './utils/analytics.js';
|
||||
|
||||
user.screenview('Node service', 'ontime').send();
|
||||
user.event('NODE', 'started', 'starting node service').send();
|
||||
|
||||
@@ -12,6 +13,7 @@ import { Low, JSONFile } from 'lowdb';
|
||||
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
@@ -26,7 +28,7 @@ import express from 'express';
|
||||
import http from 'http';
|
||||
import cors from 'cors';
|
||||
import { dbModelv1 as dbModel } from './models/dataModel.js';
|
||||
import { parseJsonv1 as parseJson, validateFile } from './utils/parser.js';
|
||||
import { parseJson_v1 as parseJson, validateFile } from './utils/parser.js';
|
||||
import ua from 'universal-analytics';
|
||||
|
||||
// validate JSON before attempting read
|
||||
@@ -86,8 +88,8 @@ app.use('/playback', playbackRouter);
|
||||
// serve react
|
||||
app.use(
|
||||
express.static(
|
||||
path.join(__dirname, env === 'prod' ? '../' : '../../', 'client/build')
|
||||
)
|
||||
path.join(__dirname, env === 'prod' ? '../' : '../../', 'client/build'),
|
||||
),
|
||||
);
|
||||
|
||||
app.get('*', (req, res) => {
|
||||
@@ -97,8 +99,8 @@ app.get('*', (req, res) => {
|
||||
env === 'prod' ? '../' : '../../',
|
||||
'client',
|
||||
'build',
|
||||
'index.html'
|
||||
)
|
||||
'index.html',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -159,8 +161,8 @@ export const startServer = async (overrideConfig = null) => {
|
||||
// OSC Config
|
||||
const oscConfig = {
|
||||
ip: oscIP,
|
||||
port: overrideConfig?.port || oscOutPort
|
||||
}
|
||||
port: overrideConfig?.port || oscOutPort,
|
||||
};
|
||||
|
||||
// init timer
|
||||
global.timer = new EventTimer(server, config.timer, oscConfig, data.http);
|
||||
|
||||
@@ -10,8 +10,8 @@ import { OSCIntegration } from './integrations/Osc.js';
|
||||
import { HTTPIntegration } from './integrations/Http.js';
|
||||
import { cleanURL } from '../utils/url.js';
|
||||
import getRandomName from '../utils/getRandomName.js';
|
||||
import { stringFromMillis } from '../utils/time.js';
|
||||
import { generateId } from '../utils/generate_id.js';
|
||||
import { stringFromMillis } from 'ontime-utils/time.js';
|
||||
import { generateId } from 'ontime-utils/generate_id.js';
|
||||
|
||||
/*
|
||||
* Class EventTimer adds functions specific to APP
|
||||
@@ -111,13 +111,20 @@ export class EventTimer extends Timer {
|
||||
* @description Shutdown process
|
||||
*/
|
||||
shutdown() {
|
||||
clearInterval(this._interval);
|
||||
this.info('SERVER', 'Shutting down ontime');
|
||||
this.info('TX', '... Closing socket server');
|
||||
this.io.close();
|
||||
this.info('TX', '... Closing OSC Client');
|
||||
this.osc.shutdown();
|
||||
this.info('TX', '... Closing HTTP Client');
|
||||
this.http.shutdown();
|
||||
if (this.io != null) {
|
||||
this.info('TX', '... Closing socket server');
|
||||
this.io.close();
|
||||
}
|
||||
if (this.osc != null) {
|
||||
this.info('TX', '... Closing OSC Client');
|
||||
this.osc.shutdown();
|
||||
}
|
||||
if (this.http != null) {
|
||||
this.info('TX', '... Closing HTTP Client');
|
||||
this.http.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import { stringFromMillis } from '../utils/time.js';
|
||||
import { stringFromMillis } from 'ontime-utils/time.js';
|
||||
|
||||
export class Timer {
|
||||
constructor() {
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
import {EventTimer} from "../EventTimer";
|
||||
import { EventTimer } from '../EventTimer';
|
||||
import http from 'http';
|
||||
import express from "express";
|
||||
import express from 'express';
|
||||
|
||||
// Create server
|
||||
const app = express();
|
||||
const server = http.createServer(app);
|
||||
|
||||
|
||||
// necessary config
|
||||
const timerConfig = {refresh: 1000};
|
||||
const timerConfig = { refresh: 1000 };
|
||||
|
||||
beforeEach(async () => {
|
||||
server.listen(0, '0.0.0.0');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
afterAll(async () => {
|
||||
await server.close();
|
||||
});
|
||||
|
||||
|
||||
test('object instantiates correctly', async () => {
|
||||
const t = new EventTimer(server, timerConfig);
|
||||
|
||||
@@ -41,16 +35,15 @@ test('object instantiates correctly', async () => {
|
||||
// and its own properties
|
||||
expect(t.ontimeCycle).toBe('idle');
|
||||
expect(t.prevCycle).toBeNull();
|
||||
expect(t.lastUpdate).toBeNull();
|
||||
expect(t.io).not.toBeNull();
|
||||
expect(t.osc).toBeNull();
|
||||
expect(t.http).toBeNull();
|
||||
expect(t._numClients).toBe(0);
|
||||
expect(t._interval).not.toBeNull();
|
||||
expect(t.presenter).toStrictEqual({text: '', visible: false});
|
||||
expect(t.public).toStrictEqual({text: '', visible: false});
|
||||
expect(t.lower).toStrictEqual({text: '', visible: false});
|
||||
expect(t.lower).toStrictEqual({text: '', visible: false});
|
||||
expect(t.presenter).toStrictEqual({ text: '', visible: false });
|
||||
expect(t.public).toStrictEqual({ text: '', visible: false });
|
||||
expect(t.lower).toStrictEqual({ text: '', visible: false });
|
||||
expect(t.lower).toStrictEqual({ text: '', visible: false });
|
||||
|
||||
const expectTitlesPublic = {
|
||||
titleNow: null,
|
||||
@@ -78,19 +71,20 @@ test('object instantiates correctly', async () => {
|
||||
expect(t.numEvents).toBe(0);
|
||||
expect(t._eventlist).toBeNull();
|
||||
expect(t.onAir).toBeFalsy();
|
||||
});
|
||||
|
||||
t.shutdown();
|
||||
});
|
||||
|
||||
describe('test triggers behaviour', () => {
|
||||
const t = new EventTimer(server, timerConfig);
|
||||
|
||||
it('ignores bad commands', () => {
|
||||
test('ignores bad commands', async(done) => {
|
||||
const success = t.trigger('test');
|
||||
expect(success).toBeFalsy();
|
||||
})
|
||||
|
||||
it('does not allow triggering events with an empty list', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
test('does not allow triggering events with an empty list', async(done) => {
|
||||
expect(t.numEvents).toBe(0);
|
||||
|
||||
expect(t.trigger('start')).toBeFalsy();
|
||||
@@ -106,10 +100,10 @@ describe('test triggers behaviour', () => {
|
||||
expect(t.onAir).toBeTruthy();
|
||||
expect(t.trigger('offAir')).toBeTruthy();
|
||||
expect(t.onAir).toBeFalsy();
|
||||
done();
|
||||
});
|
||||
|
||||
it('...and is consistent by calling the class methods', () => {
|
||||
|
||||
test('...and is consistent by calling the class methods', async (done) => {
|
||||
expect(t.numEvents).toBe(0);
|
||||
expect(t.state).toBe('stop');
|
||||
|
||||
@@ -133,5 +127,8 @@ describe('test triggers behaviour', () => {
|
||||
|
||||
t.reload();
|
||||
expect(t.state).toBe('stop');
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
t.shutdown();
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { db, data } from '../app.js';
|
||||
|
||||
// utils
|
||||
import { generateId } from '../utils/generate_id.js';
|
||||
import { generateId } from 'ontime-utils/generate_id.js';
|
||||
import {
|
||||
event as eventDef,
|
||||
delay as delayDef,
|
||||
|
||||
@@ -6,6 +6,7 @@ import { fileURLToPath } from 'url';
|
||||
import { db, data } from '../app.js';
|
||||
import { networkInterfaces } from 'os';
|
||||
import { fileHandler } from '../utils/parser.js';
|
||||
import { generateId } from 'ontime-utils/generate_id.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
@@ -116,6 +117,81 @@ export const getInfo = async (req, res) => {
|
||||
});
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/ontime/aliases'
|
||||
// Returns -
|
||||
export const getAliases = async (req, res) => {
|
||||
// send aliases array
|
||||
res.status(200).send(data.aliases);
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/ontime/aliases'
|
||||
// Returns ACK message
|
||||
export const postAliases = async (req, res) => {
|
||||
if (!req.body) {
|
||||
res.status(400).send('No object found in request');
|
||||
return;
|
||||
}
|
||||
// TODO: validate data
|
||||
try {
|
||||
const newAliases = [];
|
||||
req.body.forEach((a) => {
|
||||
newAliases.push({
|
||||
id: generateId(),
|
||||
enabled: a.enabled,
|
||||
alias: a.alias,
|
||||
pathAndParams: a.pathAndParams,
|
||||
});
|
||||
});
|
||||
data.aliases = newAliases;
|
||||
await db.write();
|
||||
res.sendStatus(200);
|
||||
} catch (error) {
|
||||
res.status(400).send(error);
|
||||
}
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/ontime/settings'
|
||||
// Returns -
|
||||
export const getSettings = async (req, res) => {
|
||||
const version = data.settings.version;
|
||||
const serverPort = data.settings.serverPort;
|
||||
const pinCode = data.settings.pinCode;
|
||||
|
||||
// send object with network information
|
||||
res.status(200).send({
|
||||
version,
|
||||
serverPort,
|
||||
pinCode,
|
||||
});
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/ontime/settings'
|
||||
// Returns ACK message
|
||||
export const postSettings = async (req, res) => {
|
||||
if (!req.body) {
|
||||
res.status(400).send('No object found in request');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let pin = data.settings.pinCode;
|
||||
if (typeof req.body?.pinCode === 'string') {
|
||||
if (req.body?.pinCode.length === 0) {
|
||||
pin = null;
|
||||
} else if (req.body?.pinCode.length <= 4) {
|
||||
pin = req.body?.pinCode;
|
||||
}
|
||||
}
|
||||
data.settings = {
|
||||
...data.settings,
|
||||
pinCode: pin,
|
||||
};
|
||||
await db.write();
|
||||
res.sendStatus(200);
|
||||
} catch (error) {
|
||||
res.status(400).send(error);
|
||||
}
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/ontime/info'
|
||||
// Returns ACK message
|
||||
export const postInfo = async (req, res) => {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export const alias = {
|
||||
enabled: false,
|
||||
alias: '',
|
||||
pathAndParams: '',
|
||||
};
|
||||
@@ -12,7 +12,9 @@ export const dbModelv1 = {
|
||||
version: 1,
|
||||
serverPort: 4001,
|
||||
lock: null,
|
||||
pinCode: null,
|
||||
},
|
||||
aliases: [],
|
||||
osc: {
|
||||
port: 8888,
|
||||
portOut: 9999,
|
||||
@@ -27,7 +29,7 @@ export const dbModelv1 = {
|
||||
url: '',
|
||||
enabled: false,
|
||||
},
|
||||
onStart: {
|
||||
onStart: {
|
||||
url: '',
|
||||
enabled: false,
|
||||
},
|
||||
@@ -35,15 +37,15 @@ export const dbModelv1 = {
|
||||
url: '',
|
||||
enabled: false,
|
||||
},
|
||||
onPause: {
|
||||
onPause: {
|
||||
url: '',
|
||||
enabled: false,
|
||||
},
|
||||
onStop: {
|
||||
onStop: {
|
||||
url: '',
|
||||
enabled: false,
|
||||
},
|
||||
onFinish: {
|
||||
onFinish: {
|
||||
url: '',
|
||||
enabled: false,
|
||||
},
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
"express": "~4.17.1",
|
||||
"express-session": "~1.17.1",
|
||||
"lowdb": "3.0.0",
|
||||
"multer": "^1.4.2",
|
||||
"nanoid": "^3.1.22",
|
||||
"node-osc": "6.1.10",
|
||||
"multer": "^1.4.4",
|
||||
"nanoid": "^3.1.30",
|
||||
"node-osc": "6.1.11",
|
||||
"node-xlsx": "^0.17.2",
|
||||
"ontime-utils": "link: ../server/utils/",
|
||||
"passport": "~0.4.1",
|
||||
"passport-local": "~1.0.0",
|
||||
"socket.io": "^4.3.1",
|
||||
"socket.io": "4.4.0",
|
||||
"universal-analytics": "^0.4.23"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import express from 'express';
|
||||
import { uploadFile } from '../utils/upload.js';
|
||||
|
||||
export const router = express.Router();
|
||||
|
||||
import {
|
||||
@@ -10,6 +11,10 @@ import {
|
||||
dbPathToUpload,
|
||||
getOSC,
|
||||
postOSC,
|
||||
getSettings,
|
||||
postSettings,
|
||||
getAliases,
|
||||
postAliases,
|
||||
} from '../controllers/ontimeController.js';
|
||||
|
||||
// create route between controller and '/ontime/db' endpoint
|
||||
@@ -18,6 +23,18 @@ router.get('/db', dbDownload);
|
||||
// create route between controller and '/ontime/db' endpoint
|
||||
router.post('/db', uploadFile, dbUpload);
|
||||
|
||||
// create route between controller and '/ontime/settings' endpoint
|
||||
router.get('/settings', getSettings);
|
||||
|
||||
// create route between controller and '/ontime/settings' endpoint
|
||||
router.post('/settings', postSettings);
|
||||
|
||||
// create route between controller and '/ontime/aliases' endpoint
|
||||
router.get('/aliases', getAliases);
|
||||
|
||||
// create route between controller and '/ontime/aliases' endpoint
|
||||
router.post('/aliases', postAliases);
|
||||
|
||||
// create route between controller and '/ontime/info' endpoint
|
||||
router.get('/info', getInfo);
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import { generateId } from '../generate_id.js';
|
||||
|
||||
describe('generate a valid id', () => {
|
||||
it('generates a 5 digit id', () => {
|
||||
const id = generateId();
|
||||
expect(id.length).toBe(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('generate 100', () => {
|
||||
it('all ids are unique', () => {
|
||||
let ids = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
ids.push(generateId());
|
||||
}
|
||||
|
||||
const unique = [...new Set(ids)];
|
||||
expect(ids.length).toBe(unique.length);
|
||||
});
|
||||
});
|
||||
@@ -1,11 +1,11 @@
|
||||
import getRandomName from '../getRandomName.js';
|
||||
|
||||
test('generates 500 unique names', () => {
|
||||
let names = [];
|
||||
for (let i = 0; i < 500; i++) {
|
||||
names.push(getRandomName());
|
||||
test('generates 100 unique names', () => {
|
||||
const names = new Set();
|
||||
let attempts = 1;
|
||||
while (names.size < 100) {
|
||||
names.add(getRandomName());
|
||||
attempts++;
|
||||
}
|
||||
|
||||
const unique = [...new Set(names)];
|
||||
expect(names.length).toBe(unique.length);
|
||||
});
|
||||
expect(attempts).toBeLessThan(105);
|
||||
});
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import jest from 'jest-mock';
|
||||
import {
|
||||
makeString,
|
||||
parseExcelv1,
|
||||
parseJsonv1,
|
||||
validateEventv1,
|
||||
parseAliases_v1,
|
||||
parseExcel_v1,
|
||||
parseJson_v1,
|
||||
validateEvent_v1,
|
||||
} from '../parser.js';
|
||||
import { dbModelv1 as dbModel } from '../../models/dataModel.js';
|
||||
|
||||
@@ -110,7 +111,7 @@ describe('test json parser with valid def', () => {
|
||||
let parseResponse;
|
||||
|
||||
beforeEach(async () => {
|
||||
parseResponse = await parseJsonv1(testData);
|
||||
parseResponse = await parseJson_v1(testData);
|
||||
});
|
||||
|
||||
it('has 7 events', () => {
|
||||
@@ -169,7 +170,7 @@ describe('test parser edge cases', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const parseResponse = await parseJsonv1(testData);
|
||||
const parseResponse = await parseJson_v1(testData);
|
||||
expect(parseResponse.events[0].id).toBeDefined();
|
||||
});
|
||||
|
||||
@@ -190,7 +191,7 @@ describe('test parser edge cases', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const parseResponse = await parseJsonv1(testData);
|
||||
const parseResponse = await parseJson_v1(testData);
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'ERROR: ID collision on import, skipping'
|
||||
);
|
||||
@@ -212,7 +213,7 @@ describe('test parser edge cases', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const parseResponse = await parseJsonv1(testData);
|
||||
const parseResponse = await parseJson_v1(testData);
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'ERROR: undefined event type, skipping'
|
||||
);
|
||||
@@ -228,7 +229,7 @@ describe('test parser edge cases', () => {
|
||||
},
|
||||
};
|
||||
|
||||
await parseJsonv1(testData);
|
||||
await parseJson_v1(testData);
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'ERROR: unknown app version, skipping'
|
||||
);
|
||||
@@ -271,7 +272,7 @@ describe('test corrupt data', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const parsedDef = await parseJsonv1(emptyEvents);
|
||||
const parsedDef = await parseJson_v1(emptyEvents);
|
||||
expect(parsedDef.events.length).toBe(2);
|
||||
});
|
||||
|
||||
@@ -293,7 +294,7 @@ describe('test corrupt data', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const parsedDef = await parseJsonv1(emptyEvents);
|
||||
const parsedDef = await parseJson_v1(emptyEvents);
|
||||
expect(parsedDef.events.length).toBe(0);
|
||||
});
|
||||
|
||||
@@ -309,7 +310,7 @@ describe('test corrupt data', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const parsedDef = await parseJsonv1(emptyEventData);
|
||||
const parsedDef = await parseJson_v1(emptyEventData);
|
||||
expect(parsedDef.event).toStrictEqual(dbModel.event);
|
||||
});
|
||||
|
||||
@@ -323,14 +324,14 @@ describe('test corrupt data', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const parsedDef = await parseJsonv1(missingSettings);
|
||||
const parsedDef = await parseJson_v1(missingSettings);
|
||||
expect(parsedDef.settings).toStrictEqual(dbModel.settings);
|
||||
});
|
||||
|
||||
it('fails with invalid JSON', async () => {
|
||||
console.log = jest.fn();
|
||||
const invalidJSON = 'some random dataset';
|
||||
const parsedDef = await parseJsonv1(invalidJSON);
|
||||
const parsedDef = await parseJson_v1(invalidJSON);
|
||||
expect(console.log).toHaveBeenCalledWith('ERROR: Invalid JSON format');
|
||||
expect(parsedDef).toBe(-1);
|
||||
});
|
||||
@@ -341,7 +342,7 @@ describe('test event validator', () => {
|
||||
const event = {
|
||||
title: 'test',
|
||||
};
|
||||
const validated = validateEventv1(event);
|
||||
const validated = validateEvent_v1(event);
|
||||
|
||||
expect(validated).toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -361,7 +362,7 @@ describe('test event validator', () => {
|
||||
|
||||
it('fails an empty object', () => {
|
||||
const event = {};
|
||||
const validated = validateEventv1(event);
|
||||
const validated = validateEvent_v1(event);
|
||||
expect(validated).toEqual(null);
|
||||
});
|
||||
|
||||
@@ -372,7 +373,7 @@ describe('test event validator', () => {
|
||||
presenter: 3.2,
|
||||
note: '1899-12-30T08:00:10.000Z',
|
||||
};
|
||||
const validated = validateEventv1(event);
|
||||
const validated = validateEvent_v1(event);
|
||||
expect(typeof validated.title).toEqual('string');
|
||||
expect(typeof validated.subtitle).toEqual('string');
|
||||
expect(typeof validated.presenter).toEqual('string');
|
||||
@@ -384,7 +385,7 @@ describe('test event validator', () => {
|
||||
timeStart: false,
|
||||
timeEnd: '2',
|
||||
};
|
||||
const validated = validateEventv1(event);
|
||||
const validated = validateEvent_v1(event);
|
||||
expect(typeof validated.timeStart).toEqual('number');
|
||||
expect(validated.timeStart).toEqual(0);
|
||||
expect(typeof validated.timeEnd).toEqual('number');
|
||||
@@ -395,7 +396,7 @@ describe('test event validator', () => {
|
||||
const event = {
|
||||
title: {},
|
||||
};
|
||||
const validated = validateEventv1(event);
|
||||
const validated = validateEvent_v1(event);
|
||||
expect(typeof validated.title).toEqual('string');
|
||||
});
|
||||
});
|
||||
@@ -485,10 +486,34 @@ describe('test parseExcel function', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const parsedData = await parseExcelv1(testdata);
|
||||
const parsedData = await parseExcel_v1(testdata);
|
||||
|
||||
expect(parsedData.events).toBeDefined();
|
||||
expect(parsedData.events).toStrictEqual(expectedParsedEvents);
|
||||
expect(parsedData.events).toStrictEqual(expectedParsedEvents);
|
||||
});
|
||||
});
|
||||
|
||||
describe('test aliases import', () => {
|
||||
it('imports a well defined alias', () => {});
|
||||
const testData = {
|
||||
events: [],
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
},
|
||||
aliases: [
|
||||
{
|
||||
enabled: false,
|
||||
alias: 'testalias',
|
||||
pathAndParams: 'testpathAndParams',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const parsed = parseAliases_v1(testData, true);
|
||||
expect(parsed.length).toBe(1);
|
||||
|
||||
// generates missing id
|
||||
expect(parsed[0].id).toBeDefined();
|
||||
});
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
import { excelDateStringToMillis, stringFromMillis } from '../time.js';
|
||||
|
||||
describe('test string to milis function', () => {
|
||||
it('test with null values', () => {
|
||||
const t = { val: null, result: '...' };
|
||||
expect(stringFromMillis(t.val)).toBe(t.result);
|
||||
});
|
||||
|
||||
it('test with valid millis', () => {
|
||||
const t = { val: 3600000, result: '01:00:00' };
|
||||
expect(stringFromMillis(t.val)).toBe(t.result);
|
||||
});
|
||||
|
||||
it('test with negative millis', () => {
|
||||
const t = { val: -3600000, result: '-01:00:00' };
|
||||
expect(stringFromMillis(t.val)).toBe(t.result);
|
||||
});
|
||||
|
||||
it('test with -1', () => {
|
||||
const t = { val: -1, result: '-00:00:00' };
|
||||
expect(stringFromMillis(t.val)).toBe(t.result);
|
||||
});
|
||||
|
||||
it('test with 0', () => {
|
||||
const t = { val: 0, result: '00:00:00' };
|
||||
expect(stringFromMillis(t.val)).toBe(t.result);
|
||||
});
|
||||
|
||||
it('test with -0', () => {
|
||||
const t = { val: -0, result: '00:00:00' };
|
||||
expect(stringFromMillis(t.val)).toBe(t.result);
|
||||
});
|
||||
|
||||
it('test with 999', () => {
|
||||
const t = { val: 999, result: '00:00:00' };
|
||||
expect(stringFromMillis(t.val)).toBe(t.result);
|
||||
});
|
||||
|
||||
it('test with 1000', () => {
|
||||
const t = { val: 1000, result: '00:00:01' };
|
||||
expect(stringFromMillis(t.val)).toBe(t.result);
|
||||
});
|
||||
|
||||
it('test with 86400000 (24 hours)', () => {
|
||||
const t = { val: 86400000, result: '00:00:00' };
|
||||
expect(stringFromMillis(t.val)).toBe(t.result);
|
||||
});
|
||||
|
||||
it('test with 86401000 (24 hours and 1 second)', () => {
|
||||
const t = { val: 86401000, result: '00:00:01' };
|
||||
expect(stringFromMillis(t.val)).toBe(t.result);
|
||||
});
|
||||
|
||||
it('test with -86401000 (-24 hours and 1 second)', () => {
|
||||
const t = { val: -86401000, result: '-00:00:01' };
|
||||
expect(stringFromMillis(t.val)).toBe(t.result);
|
||||
});
|
||||
});
|
||||
|
||||
describe('test excel date parser', () => {
|
||||
it('parses the given dates correctly', () => {
|
||||
const d0 = '1899-12-30T00:00:00.000Z';
|
||||
const d1 = '1899-12-30T08:00:00.000Z';
|
||||
const d2 = '1899-12-30T08:30:00.000Z';
|
||||
|
||||
const d0Millis = 0;
|
||||
const d1Millis = 28800000;
|
||||
const d2Millis = 30600000;
|
||||
|
||||
expect(excelDateStringToMillis(d0)).toBe(d0Millis);
|
||||
expect(excelDateStringToMillis(d1)).toBe(d1Millis);
|
||||
expect(excelDateStringToMillis(d2)).toBe(d2Millis);
|
||||
});
|
||||
|
||||
it('handles an invalid date string', () => {
|
||||
const s = 'hello';
|
||||
expect(excelDateStringToMillis(s)).toBe(null);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +0,0 @@
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('1234567890abcdef', 5);
|
||||
|
||||
export const generateId = () => nanoid();
|
||||
+210
-128
@@ -6,8 +6,8 @@ import {
|
||||
block as blockDef,
|
||||
} from '../models/eventsDefinition.js';
|
||||
import { dbModelv1 } from '../models/dataModel.js';
|
||||
import { generateId } from './generate_id.js';
|
||||
import { excelDateStringToMillis } from './time.js';
|
||||
import { generateId } from 'ontime-utils/generate_id.js';
|
||||
import { excelDateStringToMillis } from 'ontime-utils/time.js';
|
||||
|
||||
export const EXCEL_MIME =
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||
@@ -36,8 +36,8 @@ export const fileHandler = async (file) => {
|
||||
|
||||
// we only look at worksheets called ontime or event schedule
|
||||
if (excelData?.data) {
|
||||
const dataFromExcel = await parseExcelv1(excelData.data);
|
||||
res.data = await parseJsonv1(dataFromExcel);
|
||||
const dataFromExcel = await parseExcel_v1(excelData.data);
|
||||
res.data = await parseJson_v1(dataFromExcel);
|
||||
res.message = 'success';
|
||||
} else {
|
||||
console.log('Error: No sheets found named ontime or event schedule');
|
||||
@@ -64,7 +64,7 @@ export const fileHandler = async (file) => {
|
||||
|
||||
if (uploadedJson.settings.version === 1) {
|
||||
try {
|
||||
res.data = await parseJsonv1(uploadedJson);
|
||||
res.data = await parseJson_v1(uploadedJson);
|
||||
res.message = 'success';
|
||||
} catch (error) {
|
||||
res = { error: true, message: `Error parsing file: ${error}` };
|
||||
@@ -75,7 +75,7 @@ export const fileHandler = async (file) => {
|
||||
}
|
||||
|
||||
// delete file
|
||||
deleteFile(file);
|
||||
await deleteFile(file);
|
||||
|
||||
return res;
|
||||
};
|
||||
@@ -85,7 +85,7 @@ export const fileHandler = async (file) => {
|
||||
* @param {array} excelData - array with excel sheet
|
||||
* @returns {object} - parsed object
|
||||
*/
|
||||
export const parseExcelv1 = async (excelData) => {
|
||||
export const parseExcel_v1 = async (excelData) => {
|
||||
let eventData = {
|
||||
title: '',
|
||||
url: '',
|
||||
@@ -200,136 +200,27 @@ export const parseExcelv1 = async (excelData) => {
|
||||
* @param {boolean} [enforce=false] - flag, tells to create an object anyway
|
||||
* @returns {object} - parsed object
|
||||
*/
|
||||
|
||||
export const parseJsonv1 = async (jsonData, enforce=false) => {
|
||||
export const parseJson_v1 = async (jsonData, enforce = false) => {
|
||||
if (!jsonData || typeof jsonData !== 'object') {
|
||||
console.log('ERROR: Invalid JSON format');
|
||||
return -1;
|
||||
}
|
||||
|
||||
let numEntries = 0;
|
||||
// object containing the parsed data
|
||||
let returnData = {};
|
||||
if ('events' in jsonData) {
|
||||
console.log('Found events definition, importing...');
|
||||
let events = [];
|
||||
let ids = [];
|
||||
for (const e of jsonData.events) {
|
||||
// double check unique ids
|
||||
if (ids.indexOf(e?.id) !== -1) {
|
||||
console.log('ERROR: ID collision on import, skipping');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e.type === 'event') {
|
||||
let event = validateEventv1(e);
|
||||
if (event != null) {
|
||||
events.push(event);
|
||||
ids.push(event.id);
|
||||
numEntries++;
|
||||
}
|
||||
} else if (e.type === 'delay') {
|
||||
events.push({
|
||||
...delayDef,
|
||||
duration: e.duration,
|
||||
id: e.id || generateId(),
|
||||
});
|
||||
numEntries++;
|
||||
} else if (e.type === 'block') {
|
||||
events.push({ ...blockDef, id: e.id || generateId() });
|
||||
numEntries++;
|
||||
} else {
|
||||
console.log('ERROR: undefined event type, skipping');
|
||||
}
|
||||
}
|
||||
// write to db
|
||||
returnData.events = events;
|
||||
console.log(`Uploaded file with ${numEntries} entries`);
|
||||
} else if (enforce) {
|
||||
returnData.events = [];
|
||||
console.log(`Created events object in db`);
|
||||
}
|
||||
|
||||
if ('event' in jsonData) {
|
||||
console.log('Found event data, importing...');
|
||||
const e = jsonData.event;
|
||||
|
||||
// filter known properties and write to db
|
||||
returnData.event = {
|
||||
...dbModelv1.event,
|
||||
title: e.title || dbModelv1.event.title,
|
||||
url: e.url || dbModelv1.event.url,
|
||||
publicInfo: e.publicInfo || dbModelv1.event.publicInfo,
|
||||
backstageInfo: e.backstageInfo || dbModelv1.event.backstageInfo,
|
||||
endMessage: e.endMessage || dbModelv1.event.endMessage,
|
||||
};
|
||||
} else if (enforce) {
|
||||
returnData.event = dbModelv1.event;
|
||||
console.log(`Created event object in db`);
|
||||
}
|
||||
|
||||
// parse Events
|
||||
returnData.events = parseEvents_v1(jsonData);
|
||||
// parse Event
|
||||
returnData.event = parseEvent_v1(jsonData, enforce);
|
||||
// Settings handled partially
|
||||
if ('settings' in jsonData) {
|
||||
console.log('Found settings definition, importing...');
|
||||
const s = jsonData.settings;
|
||||
|
||||
// skip if file definition is missing
|
||||
if (s.app == null || s.version == null) {
|
||||
console.log('ERROR: unknown app version, skipping');
|
||||
} else {
|
||||
let settings = {
|
||||
lock: s.lock || null,
|
||||
};
|
||||
|
||||
// write to db
|
||||
returnData.settings = {
|
||||
...dbModelv1.settings,
|
||||
...settings,
|
||||
};
|
||||
}
|
||||
} else if (enforce) {
|
||||
returnData.settings = dbModelv1.settings;
|
||||
console.log(`Created settings object in db`);
|
||||
}
|
||||
|
||||
returnData.settings = parseSettings_v1(jsonData, enforce);
|
||||
// Import OSC settings if any
|
||||
if ('osc' in jsonData) {
|
||||
console.log('Found OSC definition, importing...');
|
||||
const s = jsonData.osc;
|
||||
let osc = {};
|
||||
|
||||
if (s.port) osc.port = s.port;
|
||||
if (s.portOut) osc.portOut = s.portOut;
|
||||
if (s.targetIP) osc.targetIP = s.targetIP;
|
||||
if (s.enabled) osc.enabled = s.enabled;
|
||||
|
||||
// write to db
|
||||
returnData.osc = {
|
||||
...dbModelv1.osc,
|
||||
...osc,
|
||||
};
|
||||
} else if (enforce) {
|
||||
returnData.osc = dbModelv1.osc;
|
||||
console.log(`Created osc object in db`);
|
||||
}
|
||||
|
||||
returnData.osc = parseOsc_v1(jsonData, enforce);
|
||||
// Import HTTP settings if any
|
||||
if ('http' in jsonData) {
|
||||
console.log('Found HTTP definition, importing...');
|
||||
const h = jsonData.osc;
|
||||
let http = {};
|
||||
|
||||
if (h.user) http.user = h.user;
|
||||
if (h.pwd) http.pwd = h.pwd;
|
||||
|
||||
// write to db
|
||||
returnData.http = {
|
||||
...dbModelv1.http,
|
||||
...http,
|
||||
};
|
||||
} else if (enforce) {
|
||||
returnData.http = dbModelv1.http;
|
||||
console.log(`Created http object in db`);
|
||||
}
|
||||
returnData.http = parseHttp_v1(jsonData, enforce);
|
||||
// Import Aliases if any
|
||||
returnData.aliases = parseAliases_v1(jsonData);
|
||||
|
||||
return returnData;
|
||||
};
|
||||
@@ -352,7 +243,7 @@ export const makeString = (val, fallback = '') => {
|
||||
* @returns {object|null} - formatted object or null in case is invalid
|
||||
*/
|
||||
|
||||
export const validateEventv1 = (eventArgs) => {
|
||||
export const validateEvent_v1 = (eventArgs) => {
|
||||
// ensure id is defined and unique
|
||||
const id = eventArgs.id || generateId();
|
||||
let event = null;
|
||||
@@ -418,3 +309,194 @@ export const validateFile = (file) => {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse events array of an entry
|
||||
* @param {object} data - data object
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseEvents_v1 = (data) => {
|
||||
let newEvents = [];
|
||||
if ('events' in data) {
|
||||
console.log('Found events definition, importing...');
|
||||
let events = [];
|
||||
let ids = [];
|
||||
for (const e of data.events) {
|
||||
// double check unique ids
|
||||
if (ids.indexOf(e?.id) !== -1) {
|
||||
console.log('ERROR: ID collision on import, skipping');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e.type === 'event') {
|
||||
let event = validateEvent_v1(e);
|
||||
if (event != null) {
|
||||
events.push(event);
|
||||
ids.push(event.id);
|
||||
}
|
||||
} else if (e.type === 'delay') {
|
||||
events.push({
|
||||
...delayDef,
|
||||
duration: e.duration,
|
||||
id: e.id || generateId(),
|
||||
});
|
||||
} else if (e.type === 'block') {
|
||||
events.push({ ...blockDef, id: e.id || generateId() });
|
||||
} else {
|
||||
console.log('ERROR: undefined event type, skipping');
|
||||
}
|
||||
}
|
||||
// write to db
|
||||
newEvents = events;
|
||||
console.log(`Uploaded file with ${events.length} entries`);
|
||||
}
|
||||
return newEvents;
|
||||
};
|
||||
/**
|
||||
* Parse event portion of an entry
|
||||
* @param {object} data - data object
|
||||
* @param {boolean} enforce - whether to create a definition if one is missing
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseEvent_v1 = (data, enforce) => {
|
||||
let newEvent = {};
|
||||
if ('event' in data) {
|
||||
console.log('Found event data, importing...');
|
||||
const e = data.event;
|
||||
// filter known properties and write to db
|
||||
newEvent = {
|
||||
...dbModelv1.event,
|
||||
title: e.title || dbModelv1.event.title,
|
||||
url: e.url || dbModelv1.event.url,
|
||||
publicInfo: e.publicInfo || dbModelv1.event.publicInfo,
|
||||
backstageInfo: e.backstageInfo || dbModelv1.event.backstageInfo,
|
||||
endMessage: e.endMessage || dbModelv1.event.endMessage,
|
||||
};
|
||||
} else if (enforce) {
|
||||
newEvent = dbModelv1.event;
|
||||
console.log(`Created event object in db`);
|
||||
}
|
||||
return newEvent;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse settings portion of an entry
|
||||
* @param {object} data - data object
|
||||
* @param {boolean} enforce - whether to create a definition if one is missing
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseSettings_v1 = (data, enforce) => {
|
||||
let newSettings = {};
|
||||
if ('settings' in data) {
|
||||
console.log('Found settings definition, importing...');
|
||||
const s = data.settings;
|
||||
|
||||
// skip if file definition is missing
|
||||
if (s.app == null || s.version == null) {
|
||||
console.log('ERROR: unknown app version, skipping');
|
||||
} else {
|
||||
let settings = {
|
||||
lock: s.lock || null,
|
||||
pinCode: s.pinCode || null,
|
||||
};
|
||||
|
||||
// write to db
|
||||
newSettings = {
|
||||
...dbModelv1.settings,
|
||||
...settings,
|
||||
};
|
||||
}
|
||||
} else if (enforce) {
|
||||
newSettings = dbModelv1.settings;
|
||||
console.log(`Created settings object in db`);
|
||||
}
|
||||
return newSettings;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse osc portion of an entry
|
||||
* @param {object} data - data object
|
||||
* @param {boolean} enforce - whether to create a definition if one is missing
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseOsc_v1 = (data, enforce) => {
|
||||
let newOsc = {};
|
||||
if ('osc' in data) {
|
||||
console.log('Found OSC definition, importing...');
|
||||
const s = data.osc;
|
||||
let osc = {};
|
||||
|
||||
if (s.port) osc.port = s.port;
|
||||
if (s.portOut) osc.portOut = s.portOut;
|
||||
if (s.targetIP) osc.targetIP = s.targetIP;
|
||||
if (s.enabled) osc.enabled = s.enabled;
|
||||
|
||||
// write to db
|
||||
newOsc = {
|
||||
...dbModelv1.osc,
|
||||
...osc,
|
||||
};
|
||||
} else if (enforce) {
|
||||
newOsc = dbModelv1.osc;
|
||||
console.log(`Created OSC object in db`);
|
||||
}
|
||||
return newOsc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse Http portion of an entry
|
||||
* @param {object} data - data object
|
||||
* @param {boolean} enforce - whether to create a definition if one is missing
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseHttp_v1 = (data, enforce) => {
|
||||
let newHttp = {};
|
||||
if ('http' in data) {
|
||||
console.log('Found HTTP definition, importing...');
|
||||
const h = data.osc;
|
||||
let http = {};
|
||||
|
||||
if (h.user) http.user = h.user;
|
||||
if (h.pwd) http.pwd = h.pwd;
|
||||
|
||||
// write to db
|
||||
newHttp.http = {
|
||||
...dbModelv1.http,
|
||||
...http,
|
||||
};
|
||||
} else if (enforce) {
|
||||
newHttp.http = dbModelv1.http;
|
||||
console.log(`Created http object in db`);
|
||||
}
|
||||
return newHttp;
|
||||
};
|
||||
/**
|
||||
* Parse aliases portion of an entry
|
||||
* @param {object} data - data object
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseAliases_v1 = (data) => {
|
||||
let newAliases = [];
|
||||
if ('aliases' in data) {
|
||||
console.log('Found Aliases definition, importing...');
|
||||
const ids = [];
|
||||
for (const a of data.aliases) {
|
||||
// double check unique ids
|
||||
if (ids.indexOf(a?.id) !== -1) {
|
||||
console.log('ERROR: ID collision on import, skipping');
|
||||
continue;
|
||||
}
|
||||
const newAlias = {
|
||||
id: a.id || generateId(),
|
||||
enabled: a.enabled || false,
|
||||
alias: a.alias || '',
|
||||
pathAndParams: a.pathAndParams || '',
|
||||
};
|
||||
|
||||
ids.push(newAlias.id);
|
||||
newAliases.push(newAlias);
|
||||
}
|
||||
console.log(`Uploaded ${newAliases?.length || 0} alias(es)`);
|
||||
}
|
||||
return newAliases;
|
||||
};
|
||||
|
||||
+27
-23
@@ -460,10 +460,10 @@ engine.io-parser@~5.0.0:
|
||||
dependencies:
|
||||
base64-arraybuffer "~1.0.1"
|
||||
|
||||
engine.io@~6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.0.0.tgz#2b993fcd73e6b3a6abb52b40b803651cd5747cf0"
|
||||
integrity sha512-Ui7yl3JajEIaACg8MOUwWvuuwU7jepZqX3BKs1ho7NQRuP4LhN4XIykXhp8bEy+x/DhA0LBZZXYSCkZDqrwMMg==
|
||||
engine.io@~6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.1.0.tgz#459eab0c3724899d7b63a20c3a6835cf92857939"
|
||||
integrity sha512-ErhZOVu2xweCjEfYcTdkCnEYUiZgkAcBBAhW4jbIvNG8SLU3orAqoJCiytZjYF7eTpVmmCrLDjLIEaPlUAs1uw==
|
||||
dependencies:
|
||||
"@types/cookie" "^0.4.1"
|
||||
"@types/cors" "^2.8.12"
|
||||
@@ -1059,10 +1059,10 @@ ms@2.1.2:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
multer@^1.4.2:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.3.tgz#4db352d6992e028ac0eacf7be45c6efd0264297b"
|
||||
integrity sha512-np0YLKncuZoTzufbkM6wEKp68EhWJXcU6fq6QqrSwkckd2LlMgd1UqhUJLj6NS/5sZ8dE8LYDWslsltJznnXlg==
|
||||
multer@^1.4.4:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.4.tgz#e2bc6cac0df57a8832b858d7418ccaa8ebaf7d8c"
|
||||
integrity sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==
|
||||
dependencies:
|
||||
append-field "^1.0.0"
|
||||
busboy "^0.2.11"
|
||||
@@ -1073,7 +1073,7 @@ multer@^1.4.2:
|
||||
type-is "^1.6.4"
|
||||
xtend "^4.0.0"
|
||||
|
||||
nanoid@^3.1.22:
|
||||
nanoid@^3.1.30:
|
||||
version "3.1.30"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362"
|
||||
integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==
|
||||
@@ -1088,10 +1088,10 @@ negotiator@0.6.2:
|
||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
|
||||
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
|
||||
|
||||
node-osc@6.1.10:
|
||||
version "6.1.10"
|
||||
resolved "https://registry.yarnpkg.com/node-osc/-/node-osc-6.1.10.tgz#f618281f1f4fe91a41bc1414f58322369011e1ed"
|
||||
integrity sha512-HiDNie6meSzfUcmso8jGyeQ1TTWC2G9FI/vO57H5JaNGAUCCh9X8wDzTZy0KpDTSBxxNC2Rgr8kCC7f0oJ/gww==
|
||||
node-osc@6.1.11:
|
||||
version "6.1.11"
|
||||
resolved "https://registry.yarnpkg.com/node-osc/-/node-osc-6.1.11.tgz#c1d0700cb74e7fe141f4f99531318b8615e38d2f"
|
||||
integrity sha512-bwFV/UKBBaSNK5pLYeA8Qx0GVc49fKRjmfMwMoypfPwb7RdhyRccW+tTNHcwr/OJ2b01uVKpnTPurSNs4fWnvw==
|
||||
dependencies:
|
||||
osc-min "^1.1.1"
|
||||
|
||||
@@ -1133,6 +1133,10 @@ once@^1.3.0:
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
"ontime-utils@link: ../server/utils":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
optionator@^0.9.1:
|
||||
version "0.9.1"
|
||||
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
|
||||
@@ -1426,10 +1430,10 @@ shebang-regex@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
|
||||
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
|
||||
|
||||
socket.io-adapter@~2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.3.2.tgz#039cd7c71a52abad984a6d57da2c0b7ecdd3c289"
|
||||
integrity sha512-PBZpxUPYjmoogY0aoaTmo1643JelsaS1CiAwNjRVdrI0X9Seuc19Y2Wife8k88avW6haG8cznvwbubAZwH4Mtg==
|
||||
socket.io-adapter@~2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz#4d6111e4d42e9f7646e365b4f578269821f13486"
|
||||
integrity sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==
|
||||
|
||||
socket.io-parser@~4.0.4:
|
||||
version "4.0.4"
|
||||
@@ -1440,16 +1444,16 @@ socket.io-parser@~4.0.4:
|
||||
component-emitter "~1.3.0"
|
||||
debug "~4.3.1"
|
||||
|
||||
socket.io@^4.3.1:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.3.1.tgz#c0aa14f3f916a8ab713e83a5bd20c16600245763"
|
||||
integrity sha512-HC5w5Olv2XZ0XJ4gOLGzzHEuOCfj3G0SmoW3jLHYYh34EVsIr3EkW9h6kgfW+K3TFEcmYy8JcPWe//KUkBp5jA==
|
||||
socket.io@4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.4.0.tgz#8140a0db2c22235f88a6dceb867e4d5c9bd70507"
|
||||
integrity sha512-bnpJxswR9ov0Bw6ilhCvO38/1WPtE3eA2dtxi2Iq4/sFebiDJQzgKNYA7AuVVdGW09nrESXd90NbZqtDd9dzRQ==
|
||||
dependencies:
|
||||
accepts "~1.3.4"
|
||||
base64id "~2.0.0"
|
||||
debug "~4.3.2"
|
||||
engine.io "~6.0.0"
|
||||
socket.io-adapter "~2.3.2"
|
||||
engine.io "~6.1.0"
|
||||
socket.io-adapter "~2.3.3"
|
||||
socket.io-parser "~4.0.4"
|
||||
|
||||
ssf@~0.11.2:
|
||||
|
||||
Reference in New Issue
Block a user