diff --git a/lib/api-functions.ts b/lib/api-functions.ts index eeeb2a2..63927cf 100644 --- a/lib/api-functions.ts +++ b/lib/api-functions.ts @@ -94,7 +94,7 @@ export async function connectionRequest( else if (name === "password") return password; const p = device[name]; - if (p && p.value) return p.value[0]; + if (p?.value) return p.value[0]; } else if (exp[0] === "FUNC") { if (exp[1] === "REMOTE_ADDRESS") return remoteAddress; else if (exp[1] === "USERNAME") return username; @@ -317,7 +317,7 @@ function sanitizeTask(task): void { export async function insertTasks(tasks: any[]): Promise { if (tasks && !Array.isArray(tasks)) tasks = [tasks]; - else if (!tasks || tasks.length === 0) return tasks || []; + else if (!tasks?.length) return tasks || []; for (const task of tasks) { sanitizeTask(task); diff --git a/lib/cache.ts b/lib/cache.ts index feb0254..2bc539c 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -59,7 +59,7 @@ export async function set( export async function pop(key: string): Promise { const res = await cacheCollection.findOneAndDelete({ _id: key }); - if (res && res["value"]) return res["value"]["value"]; + if (res?.["value"]) return res["value"]["value"]; return null; } diff --git a/lib/cwmp.ts b/lib/cwmp.ts index d1b9fb6..5cc5d2c 100644 --- a/lib/cwmp.ts +++ b/lib/cwmp.ts @@ -93,7 +93,7 @@ async function authenticate( } } - if (authentication && authentication.method === "Digest") { + if (authentication?.method === "Digest") { const sessionNonce = sessionsNonces.get(sessionContext.httpRequest.socket); if ( @@ -434,7 +434,7 @@ async function applyPresets(sessionContext: SessionContext): Promise { const deviceEvents = {}; for (const p of deviceData.paths.find(Path.parse("Events.*"), false, true)) { const attrs = deviceData.attributes.get(p); - if (attrs && attrs.value && attrs.value[1][0] >= sessionContext.timestamp) + if (attrs?.value && attrs.value[1][0] >= sessionContext.timestamp) deviceEvents[p.segments[1] as string] = true; } @@ -458,7 +458,7 @@ async function applyPresets(sessionContext: SessionContext): Promise { if (!eventsMatch) continue; - if (preset.schedule && preset.schedule.schedule) { + if (preset.schedule?.schedule) { const r = scheduling.cron( sessionContext.timestamp, preset.schedule.schedule @@ -786,7 +786,7 @@ async function endSession(sessionContext: SessionContext): Promise { } } - if (sessionContext.doneTasks && sessionContext.doneTasks.length) { + if (sessionContext.doneTasks?.length) { saveCache = true; promises.push( db.clearTasks(sessionContext.deviceId, sessionContext.doneTasks) @@ -1082,7 +1082,7 @@ async function processRequest( } if (sessionContext.state === 0) { - if (!rpc.cpeRequest || rpc.cpeRequest.name !== "Inform") + if (rpc.cpeRequest?.name !== "Inform") return reportBadState(sessionContext); const res = await inform(sessionContext, rpc); @@ -1549,7 +1549,7 @@ async function listenerAsync( if (sessionContext) return processRequest(sessionContext, rpc, parseWarnings, bodyStr); - if (!(rpc.cpeRequest && rpc.cpeRequest.name === "Inform")) { + if (rpc.cpeRequest?.name !== "Inform") { logger.accessError({ message: "Invalid session", sessionContext: { diff --git a/lib/db.ts b/lib/db.ts index 71ece72..52d1419 100644 --- a/lib/db.ts +++ b/lib/db.ts @@ -505,7 +505,7 @@ export async function saveDevice( break; case "object": - if (!diff[1] || !diff[1].object || object2 !== object1) { + if (!diff[1]?.object || object2 !== object1) { update["$set"][ path.length ? path.toString() + "._object" : "_object" ] = !!object2; @@ -513,7 +513,7 @@ export async function saveDevice( break; case "writable": - if (!diff[1] || !diff[1].writable || writable2 !== writable1) { + if (!diff[1]?.writable || writable2 !== writable1) { update["$set"][ path.length ? path.toString() + "._writable" : "_writable" ] = !!writable2; @@ -566,7 +566,7 @@ export async function saveDevice( for (const attrName of Object.keys(diff[1])) { if ( diff[1][attrName][1] != null && - (!diff[2] || !diff[2][attrName] || diff[2][attrName][1] == null) + diff[2]?.[attrName]?.[1] == null ) { const p = path.length ? path.toString() + "." : ""; update["$unset"][`${p}_${attrName}`] = 1; diff --git a/lib/device.ts b/lib/device.ts index ee94642..96ca975 100644 --- a/lib/device.ts +++ b/lib/device.ts @@ -208,7 +208,7 @@ export function clear( timestamp = timestamp || 0; let descendantsTimestamp = timestamp; - if (attributes && attributes.object) { + if (attributes?.object) { if (attributes.object > descendantsTimestamp) descendantsTimestamp = attributes.object; if (!(attributes.object <= attributes.value)) diff --git a/lib/local-cache.ts b/lib/local-cache.ts index 66b1612..386871a 100644 --- a/lib/local-cache.ts +++ b/lib/local-cache.ts @@ -131,7 +131,7 @@ async function fetchPresets(): Promise { // If no keys are defined, consider all parameters as keys to keep the // same behavior from v1.0 - if (!obj["_keys"] || !obj["_keys"].length) + if (!obj["_keys"]?.length) obj["_keys"] = Object.keys(obj).filter((k) => !k.startsWith("_")); return obj; @@ -499,7 +499,7 @@ export function getConfig( if (!(key in snapshot.config)) { if (key in oldOpts) { let id; - if (context && context["id"]) { + if (context?.["id"]) { id = context["id"]; } else if (cb) { id = cb(["PARAM", "DeviceID.ID"]); diff --git a/lib/session.ts b/lib/session.ts index ebbd4ba..8c5d756 100644 --- a/lib/session.ts +++ b/lib/session.ts @@ -136,7 +136,7 @@ export function configContextCallback( const path = paths.get(Path.parse(name)); if (path) { const attrs = deviceData.attributes.get(path, 1); - if (attrs && attrs.value && attrs.value[1]) return attrs.value[1][0]; + if (attrs?.value?.[1]) return attrs.value[1][0]; } } else if (exp[0] === "FUNC") { if (exp[1] === "REMOTE_ADDRESS") @@ -299,7 +299,7 @@ export async function transferComplete( if (!sessionContext.operationsTouched) sessionContext.operationsTouched = {}; sessionContext.operationsTouched[commandKey] = 1; - if (rpcReq.faultStruct && rpcReq.faultStruct.faultCode !== "0") { + if (rpcReq.faultStruct?.faultCode !== "0") { revertDownloadParameters(sessionContext, operation.args.instance); const fault: Fault = { @@ -418,13 +418,7 @@ function revertDownloadParameters( ); const toClear = device.set(sessionContext.deviceData, p, timestamp, { - value: [ - timestamp, - [ - lastDownload && lastDownload.value[1] ? lastDownload.value[1][0] : 0, - "xsd:dateTime", - ], - ], + value: [timestamp, [lastDownload?.value[1]?.[0] || 0, "xsd:dateTime"]], }); if (toClear) { @@ -1041,7 +1035,7 @@ export async function rpcRequest( if ( !sessionContext.virtualParameters.length && !sessionContext.declarations.length && - !(_declarations && _declarations.length) && + !_declarations?.length && !sessionContext.provisions.length ) return { fault: null, rpcId: null, rpc: null }; @@ -1116,7 +1110,7 @@ export async function rpcRequest( return rpcRequest(sessionContext, _declarations); } - if (_declarations && _declarations.length) { + if (_declarations?.length) { delete sessionContext.syncState; if (!sessionContext.declarations[0]) sessionContext.declarations[0] = []; sessionContext.declarations[0] = sessionContext.declarations[0].concat( @@ -1452,7 +1446,7 @@ export async function rpcRequest( for (const [p, v] of sessionContext.syncState.downloadsValues) { const attrs = sessionContext.deviceData.attributes.get(p); if (attrs) { - if (attrs.writable && attrs.writable[1] && attrs.value) { + if (attrs.writable?.[1] && attrs.value) { const val = device.sanitizeParameterValue([v, attrs.value[1][1]]); if (val[0] !== attrs.value[1][0]) { toClear = device.set( @@ -1693,7 +1687,7 @@ function generateGetRpcRequest( syncState.refreshAttributes.value.delete(path); // Need to check in case param is deleted or changed to object const attrs = sessionContext.deviceData.attributes.get(path); - if (attrs && attrs.object && attrs.object[1] === 0) { + if (attrs?.object?.[1] === 0) { parameterNames.push(path.toString()); if (parameterNames.length >= GPV_BATCH_SIZE) break; } @@ -1846,7 +1840,7 @@ function generateSetRpcRequest( for (const [k, v] of syncState.spv) { syncState.spv.delete(k); const attrs = sessionContext.deviceData.attributes.get(k); - const curVal = attrs.value ? attrs.value[1] : null; + const curVal = attrs.value?.[1]; if (curVal && canWrite(attrs)) { const val = v.slice() as [string | number | boolean, string]; if (!val[1]) val[1] = curVal[1]; @@ -1912,7 +1906,7 @@ function generateSetRpcRequest( for (const [p, t] of syncState.downloadsDownload) { if (!(t > 0 && t <= sessionContext.timestamp)) continue; const attrs = deviceData.attributes.get(p); - if (!(attrs && attrs.value && t <= attrs.value[1][0])) { + if (!(t <= attrs?.value?.[1]?.[0])) { const fileTypeAttrs = deviceData.attributes.get( deviceData.paths.get(p.slice(0, -1).concat(Path.parse("FileType"))) ); @@ -1929,21 +1923,9 @@ function generateSetRpcRequest( name: "Download", commandKey: generateRpcId(sessionContext), instance: p.segments[1] as string, - fileType: fileTypeAttrs - ? fileTypeAttrs.value - ? (fileTypeAttrs.value[1][0] as string) - : null - : null, - fileName: fileNameAttrs - ? fileNameAttrs.value - ? (fileNameAttrs.value[1][0] as string) - : null - : null, - targetFileName: targetFileNameAttrs - ? targetFileNameAttrs.value - ? (targetFileNameAttrs.value[1][0] as string) - : null - : null, + fileType: fileTypeAttrs?.value?.[1][0] as string, + fileName: fileNameAttrs?.value?.[1][0] as string, + targetFileName: targetFileNameAttrs?.value?.[1][0] as string, }; } } @@ -1952,7 +1934,7 @@ function generateSetRpcRequest( if (syncState.reboot > 0 && syncState.reboot <= sessionContext.timestamp) { const p = sessionContext.deviceData.paths.get(Path.parse("Reboot")); const attrs = p ? sessionContext.deviceData.attributes.get(p) : null; - if (!(attrs && attrs.value && attrs.value[1][0] >= syncState.reboot)) { + if (!(attrs?.value?.[1][0] >= syncState.reboot)) { delete syncState.reboot; return { name: "Reboot" }; } @@ -1965,9 +1947,7 @@ function generateSetRpcRequest( ) { const p = sessionContext.deviceData.paths.get(Path.parse("FactoryReset")); const attrs = p ? sessionContext.deviceData.attributes.get(p) : null; - if ( - !(attrs && attrs.value && attrs.value[1][0] >= syncState.factoryReset) - ) { + if (!(attrs?.value?.[1][0] >= syncState.factoryReset)) { delete syncState.factoryReset; return { name: "FactoryReset" }; } @@ -2035,7 +2015,7 @@ function generateSetVirtualParameterProvisions( let provisions; if (virtualParameterDeclarations) { for (const declaration of virtualParameterDeclarations) { - if (declaration[2] && declaration[2].value != null) { + if (declaration[2]?.value != null) { const attrs = sessionContext.deviceData.attributes.get(declaration[0]); if ( attrs && @@ -2164,9 +2144,7 @@ function processDeclarations( if (currentAttributes) { leafParam = currentPath; - leafIsObject = currentAttributes.object - ? currentAttributes.object[1] - : null; + leafIsObject = currentAttributes.object?.[1]; // Possible V8 bug causes null === 0 if (leafIsObject != null && leafIsObject === 0) leafTimestamp = Math.max(leafTimestamp, currentAttributes.object[0]); @@ -2181,13 +2159,13 @@ function processDeclarations( ) { case "Reboot": if (currentPath.length === 1) { - if (declareAttributeValues && declareAttributeValues.value) + if (declareAttributeValues?.value) syncState.reboot = +new Date(declareAttributeValues.value[0]); } break; case "FactoryReset": if (currentPath.length === 1) { - if (declareAttributeValues && declareAttributeValues.value) + if (declareAttributeValues?.value) syncState.factoryReset = +new Date(declareAttributeValues.value[0]); } break; diff --git a/lib/ui.ts b/lib/ui.ts index ac75910..dff76a1 100644 --- a/lib/ui.ts +++ b/lib/ui.ts @@ -62,7 +62,7 @@ const getAuthorizer = memoize( koa.on("error", async (err, ctx) => { setTimeout(() => { // Ignored errors resulting from aborted requests - if (ctx && ctx.req.aborted) return; + if (ctx?.req.aborted) return; throw err; }); }); @@ -95,7 +95,7 @@ koa.use( koa.use(async (ctx, next) => { let roles: string[] = []; - if (ctx.state.user && ctx.state.user.username) { + if (ctx.state.user?.username) { let user; if (ctx.state.user.authMethod === "local") { user = localCache.getUsers(ctx.state.configSnapshot)[ diff --git a/lib/ui/api-functions.ts b/lib/ui/api-functions.ts index 40823dd..c128a34 100644 --- a/lib/ui/api-functions.ts +++ b/lib/ui/api-functions.ts @@ -200,7 +200,7 @@ export function authLocal( return new Promise((resolve, reject) => { const users = getUsers(snapshot); const user = users[username]; - if (!user || !user.password) return void resolve(null); + if (!user?.password) return void resolve(null); hashPassword(password, user.salt) .then((hash) => { if (hash === user.password) resolve(true); diff --git a/lib/ui/api.ts b/lib/ui/api.ts index 8867f58..427748c 100644 --- a/lib/ui/api.ts +++ b/lib/ui/api.ts @@ -573,12 +573,12 @@ router.post("/devices/:id/tasks", async (ctx) => { if (!Array.isArray(exp)) return exp; if (exp[0] === "PARAM") { const p = device[exp[1]]; - if (p && p.value) return p.value[0]; + if (p?.value) return p.value[0]; } else if (exp[0] === "FUNC") { if (exp[1] === "REMOTE_ADDRESS") { for (const root of ["InternetGatewayDevice", "Device"]) { const p = device[`${root}.ManagementServer.ConnectionRequestURL`]; - if (p && p.value) return new URL(p.value[0]).hostname; + if (p?.value) return new URL(p.value[0]).hostname; } return null; } diff --git a/lib/ui/db.ts b/lib/ui/db.ts index 8167441..cdacbb4 100644 --- a/lib/ui/db.ts +++ b/lib/ui/db.ts @@ -190,8 +190,8 @@ export async function updateDeviceTags( const collection = db.collection("devices"); const object = {}; - if (add && add.length) object["$addToSet"] = { _tags: { $each: add } }; - if (pull && pull.length) object["$pullAll"] = { _tags: pull }; + if (add?.length) object["$addToSet"] = { _tags: { $each: add } }; + if (pull?.length) object["$pullAll"] = { _tags: pull }; await collection.updateOne({ _id: deviceId }, object); } @@ -242,7 +242,7 @@ export function putProvision( lineOffset: -1, }); } catch (err) { - if (err.stack && err.stack.startsWith(`${id}:`)) { + if (err.stack?.startsWith(`${id}:`)) { return Promise.reject( new Error(`${err.name} at ${err.stack.split("\n", 1)[0]}`) ); @@ -267,7 +267,7 @@ export function putVirtualParameter( lineOffset: -1, }); } catch (err) { - if (err.stack && err.stack.startsWith(`${id}:`)) { + if (err.stack?.startsWith(`${id}:`)) { return Promise.reject( new Error(`${err.name} at ${err.stack.split("\n", 1)[0]}`) ); diff --git a/tsconfig.json b/tsconfig.json index 5a5f8d8..05acdf5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,9 @@ { "compilerOptions": { - "lib": ["esnext", "dom"], - "module": "esnext", + "lib": ["ES2019", "dom"], + "module": "ES2020", "moduleResolution": "node", - "target": "esnext", + "target": "ES2019", "resolveJsonModule": true, "allowSyntheticDefaultImports": true, "alwaysStrict": true, diff --git a/ui/app.ts b/ui/app.ts index de2f8d8..f7c9b00 100644 --- a/ui/app.ts +++ b/ui/app.ts @@ -69,7 +69,7 @@ function pagify(pageName, page): RouteResolver { render: () => { const lastRenderTimestamp = Date.now(); let p; - if (state && state.error) p = m(errorPage.component, state); + if (state?.error) p = m(errorPage.component, state); else p = m(contextifyComponent(page.component), state); const attrs = {}; attrs["page"] = pageName; diff --git a/ui/components.ts b/ui/components.ts index 22ea558..aca53ee 100644 --- a/ui/components.ts +++ b/ui/components.ts @@ -116,7 +116,7 @@ function applyContext(vnode, parentContext): void { vnodeContext.set(vnode, vc); vnode.attrs = Object.assign({}, vc, vnode.attrs); } - if (vnode.children && vnode.children.length) + if (vnode.children?.length) for (const c of vnode.children) applyContext(c, vc); } } @@ -133,7 +133,7 @@ export function contextifyComponent(component: ComponentTypes): ComponentTypes { applyContext(res, context); return res; }; - } else if (!component.prototype || !component.prototype.view) { + } else if (!component.prototype?.view) { c = (initialNode) => { const state = (component as ClosureComponent)(initialNode); const view = state.view; diff --git a/ui/components/all-parameters.ts b/ui/components/all-parameters.ts index 3e9cff9..f1eb2c1 100644 --- a/ui/components/all-parameters.ts +++ b/ui/components/all-parameters.ts @@ -85,7 +85,7 @@ const component: ClosureComponent = (): Component => { let c = 0; for (const k of keys) { const p = device[k]; - const str = p.value && p.value[0] ? `${k} ${p.value[0]}` : k; + const str = p.value?.[0] ? `${k} ${p.value[0]}` : k; if (re && !re.test(str)) continue; ++c; if (count < limit) filteredKeys.push(k); diff --git a/ui/components/parameter.ts b/ui/components/parameter.ts index 32dbd44..a15dead 100644 --- a/ui/components/parameter.ts +++ b/ui/components/parameter.ts @@ -38,7 +38,7 @@ const evaluateParam = memoize((exp, obj, now: number) => { ) { let v = null; const p = obj[e[i][1]]; - if (p && p.value) { + if (p?.value) { v = p.value[0]; timestamp = Math.min(timestamp, p.valueTimestamp); } @@ -58,7 +58,7 @@ const evaluateParam = memoize((exp, obj, now: number) => { value = exp; } else if (exp[0] === "PARAM") { const p = obj[exp[1]]; - if (p && p.value) { + if (p?.value) { timestamp = p.valueTimestamp; value = p.value[0]; parameter = exp[1]; @@ -84,7 +84,7 @@ const component: ClosureComponent = (): Component => { if (value == null) return null; let edit; - if (device[parameter] && device[parameter].writable) { + if (device[parameter]?.writable) { edit = m( "button", { diff --git a/ui/files-page.ts b/ui/files-page.ts index 0531357..f650970 100644 --- a/ui/files-page.ts +++ b/ui/files-page.ts @@ -207,7 +207,7 @@ export const component: ClosureComponent = (): Component => { actionHandler: async (action, obj) => { if (action !== "save") throw new Error("Undefined action"); - const file = obj["file"] ? obj["file"][0] : null; + const file = obj["file"]?.[0]; // nginx strips out headers with dot, so replace with dash const headers = { diff --git a/ui/presets-page.ts b/ui/presets-page.ts index f150167..ce69521 100644 --- a/ui/presets-page.ts +++ b/ui/presets-page.ts @@ -331,7 +331,7 @@ export const component: ClosureComponent = (): Component => { overlay.open( cb, () => - !(comp.state && comp.state["current"]["modified"]) || + !comp.state?.["current"]["modified"] || confirm("You have unsaved changes. Close anyway?") ); }, diff --git a/ui/smart-query.ts b/ui/smart-query.ts index 4976464..87cc5c5 100644 --- a/ui/smart-query.ts +++ b/ui/smart-query.ts @@ -145,7 +145,7 @@ function queryTag(tag: string): Expression { export function getTip(resource: string, label: string): string { let tip; - if (resources[resource] && resources[resource][label]) { + if (resources[resource]?.[label]) { const param = resources[resource][label]; const types = resource === "devices" ? param["type"] : param["type"].split(",");