TypeScript!
This commit is contained in:
1 parent
ee8c43747e
commit
5d3c81cb23
105 files changed
+12054
-1901
No files matched your search
@@ -1,20 +1,27 @@
|
||||
"use strict";
|
||||
|
||||
import pass from "./validators/pass";
|
||||
import test from "./validators/test";
|
||||
import { PermissionSet } from "../types";
|
||||
|
||||
const validators = { pass, test };
|
||||
|
||||
export default class Authorizer {
|
||||
constructor(permissionSets) {
|
||||
private permissionSets: PermissionSet[];
|
||||
private validatorCache: WeakMap<
|
||||
object,
|
||||
(mutationType, mutation, any) => boolean
|
||||
>;
|
||||
private hasAccessCache: Map<string, boolean>;
|
||||
|
||||
public constructor(permissionSets) {
|
||||
this.permissionSets = permissionSets;
|
||||
this.validatorCache = new WeakMap();
|
||||
this.hasAccessCache = {};
|
||||
this.hasAccessCache = new Map();
|
||||
}
|
||||
|
||||
hasAccess(resourceType, access) {
|
||||
public hasAccess(resourceType, access): boolean {
|
||||
const cacheKey = `${resourceType}-${access}`;
|
||||
if (cacheKey in this.hasAccessCache) return this.hasAccessCache[cacheKey];
|
||||
if (this.hasAccessCache.has(cacheKey))
|
||||
return this.hasAccessCache.get(cacheKey);
|
||||
|
||||
let has = false;
|
||||
for (const permissionSet of this.permissionSets) {
|
||||
@@ -28,11 +35,14 @@ export default class Authorizer {
|
||||
}
|
||||
}
|
||||
|
||||
this.hasAccessCache[cacheKey] = has;
|
||||
this.hasAccessCache.set(cacheKey, has);
|
||||
return has;
|
||||
}
|
||||
|
||||
getValidator(resourceType, resource) {
|
||||
public getValidator(
|
||||
resourceType,
|
||||
resource
|
||||
): (mutationType: string, mutation: any, args: any) => boolean {
|
||||
if (this.validatorCache.has(resource))
|
||||
return this.validatorCache.get(resource);
|
||||
|
||||
@@ -53,7 +63,11 @@ export default class Authorizer {
|
||||
}
|
||||
}
|
||||
|
||||
const validator = (mutationType, mutation, any) => {
|
||||
const validator = (
|
||||
mutationType: string,
|
||||
mutation: any,
|
||||
any: any
|
||||
): boolean => {
|
||||
let valid = false;
|
||||
for (const [k, v] of Object.entries(funcs)) {
|
||||
if (v) {
|
||||
@@ -1,8 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
import { map, parseLikePattern } from "./expression-parser";
|
||||
import { Expression } from "../types";
|
||||
|
||||
function or(cnf1, cnf2) {
|
||||
type CNF = number[][];
|
||||
|
||||
function or(cnf1: CNF, cnf2: CNF): CNF {
|
||||
if (cnf1.length === 0) return cnf2;
|
||||
else if (cnf2.length === 0) return cnf1;
|
||||
|
||||
@@ -19,7 +20,30 @@ function or(cnf1, cnf2) {
|
||||
return res;
|
||||
}
|
||||
|
||||
export function booleanCnf(exp) {
|
||||
function likePatternIncludes(pat1, pat2, idx1 = 0, idx2 = 0): boolean {
|
||||
while (idx1 < pat1.length && idx2 < pat2.length) {
|
||||
if (pat1[idx1] === "\\%") {
|
||||
for (let i = idx2; i <= pat2.length; ++i)
|
||||
if (likePatternIncludes(pat1, pat2, idx1 + 1, i)) return true;
|
||||
return false;
|
||||
} else if (pat2[idx2] === "\\%") {
|
||||
if (pat1[idx1] !== "\\%") return false;
|
||||
} else if (pat1[idx1] === "\\_") {
|
||||
// Ignore
|
||||
} else if (pat2[idx2] === "\\_") {
|
||||
return false;
|
||||
} else if (pat2[idx2] !== pat1[idx1]) {
|
||||
return false;
|
||||
}
|
||||
++idx1;
|
||||
++idx2;
|
||||
}
|
||||
|
||||
if (idx1 === pat1.length && idx2 === pat2.length) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } {
|
||||
const ranges = new Map();
|
||||
const likePatterns = new Map();
|
||||
map(exp, e => {
|
||||
@@ -51,15 +75,20 @@ export function booleanCnf(exp) {
|
||||
});
|
||||
|
||||
for (const [k, v] of ranges) {
|
||||
if (k.endsWith(":number"))
|
||||
ranges.set(k, Array.from(v).sort((a, b) => a - b));
|
||||
else ranges.set(k, Array.from(v).sort());
|
||||
if (k.endsWith(":number")) {
|
||||
ranges.set(
|
||||
k,
|
||||
Array.from(v as number[]).sort((a: number, b: number) => a - b)
|
||||
);
|
||||
} else {
|
||||
ranges.set(k, Array.from(v).sort());
|
||||
}
|
||||
}
|
||||
|
||||
const expressions = new Map();
|
||||
const mutuallyExclusive = new Map();
|
||||
const expressions: Map<string, number> = new Map();
|
||||
const mutuallyExclusive: Map<string, Set<number>> = new Map();
|
||||
|
||||
function getVariable(lhs, op, rhs = "") {
|
||||
function getVariable(lhs, op, rhs = ""): number {
|
||||
const key = `${lhs}:${op}:${rhs}`;
|
||||
let f = expressions.get(key);
|
||||
if (!f) {
|
||||
@@ -72,7 +101,13 @@ export function booleanCnf(exp) {
|
||||
return f;
|
||||
}
|
||||
|
||||
function getComparisonVariables(lhs, from, fromInclusive, to, toInclusive) {
|
||||
function getComparisonVariables(
|
||||
lhs,
|
||||
from,
|
||||
fromInclusive,
|
||||
to,
|
||||
toInclusive
|
||||
): number[] {
|
||||
const type = from != null ? typeof from : typeof to;
|
||||
const range = ranges.get(`${lhs}:${type}`);
|
||||
const vars = [];
|
||||
@@ -109,7 +144,7 @@ export function booleanCnf(exp) {
|
||||
});
|
||||
}
|
||||
|
||||
function getLikeVariables(lhs, pat, esc) {
|
||||
function getLikeVariables(lhs, pat, esc): number[] {
|
||||
const vars = [];
|
||||
const pats = likePatterns.get(lhs);
|
||||
pat = pats.get(`${esc || ""}:${pat}`);
|
||||
@@ -125,7 +160,11 @@ export function booleanCnf(exp) {
|
||||
});
|
||||
}
|
||||
|
||||
const comparisonOperators = {
|
||||
interface ComparisonOperators {
|
||||
[name: string]: (v: string) => [string, boolean, string, boolean];
|
||||
}
|
||||
|
||||
const comparisonOperators: ComparisonOperators = {
|
||||
"=": v => [v, true, v, true],
|
||||
">": v => [v, false, null, null],
|
||||
">=": v => [v, true, null, null],
|
||||
@@ -133,7 +172,7 @@ export function booleanCnf(exp) {
|
||||
"<=": v => [null, null, v, true]
|
||||
};
|
||||
|
||||
function recursive(clause, negate) {
|
||||
function recursive(clause, negate): CNF {
|
||||
let op = clause[0];
|
||||
|
||||
if ((op === "AND" && !negate) || (op === "OR" && negate)) {
|
||||
@@ -218,33 +257,10 @@ export function booleanCnf(exp) {
|
||||
const cnf = recursive(exp, false);
|
||||
|
||||
for (const m of mutuallyExclusive.values()) {
|
||||
const ar = Array.from(m);
|
||||
const ar: number[] = Array.from(m);
|
||||
for (let i = 0; i < ar.length; ++i)
|
||||
for (let j = i + 1; j < ar.length; ++j) cnf.push([0 - ar[i], 0 - ar[j]]);
|
||||
}
|
||||
|
||||
return { vars: expressions.size, clauses: cnf };
|
||||
}
|
||||
|
||||
function likePatternIncludes(pat1, pat2, idx1 = 0, idx2 = 0) {
|
||||
while (idx1 < pat1.length && idx2 < pat2.length) {
|
||||
if (pat1[idx1] === "\\%") {
|
||||
for (let i = idx2; i <= pat2.length; ++i)
|
||||
if (likePatternIncludes(pat1, pat2, idx1 + 1, i)) return true;
|
||||
return false;
|
||||
} else if (pat2[idx2] === "\\%") {
|
||||
if (pat1[idx1] !== "\\%") return false;
|
||||
} else if (pat1[idx1] === "\\_") {
|
||||
// Ignore
|
||||
} else if (pat2[idx2] === "\\_") {
|
||||
return false;
|
||||
} else if (pat2[idx2] !== pat1[idx1]) {
|
||||
return false;
|
||||
}
|
||||
++idx1;
|
||||
++idx2;
|
||||
}
|
||||
|
||||
if (idx1 === pat1.length && idx2 === pat2.length) return true;
|
||||
return false;
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
"use strict";
|
||||
|
||||
import parsimmon from "parsimmon";
|
||||
import * as parsimmon from "parsimmon";
|
||||
import { Expression } from "../types";
|
||||
|
||||
// Turn escaped characters into real ones (e.g. "\\n" becomes "\n").
|
||||
function interpretEscapes(str) {
|
||||
function interpretEscapes(str): string {
|
||||
const escapes = {
|
||||
b: "\b",
|
||||
f: "\f",
|
||||
@@ -22,7 +21,7 @@ function interpretEscapes(str) {
|
||||
});
|
||||
}
|
||||
|
||||
export function map(exp, callback) {
|
||||
export function map(exp, callback): Expression {
|
||||
if (!Array.isArray(exp)) return callback(exp);
|
||||
|
||||
let clone;
|
||||
@@ -37,7 +36,7 @@ export function map(exp, callback) {
|
||||
return callback(clone || exp);
|
||||
}
|
||||
|
||||
function binaryLeft(operatorsParser, nextParser) {
|
||||
function binaryLeft(operatorsParser, nextParser): parsimmon.Parser<{}> {
|
||||
return parsimmon.seqMap(
|
||||
nextParser,
|
||||
parsimmon.seq(operatorsParser, nextParser).many(),
|
||||
@@ -257,7 +256,7 @@ const lang = parsimmon.createLanguage({
|
||||
);
|
||||
},
|
||||
Expression: function(r) {
|
||||
function unary(operatorsParser, nextParser) {
|
||||
function unary(operatorsParser, nextParser): parsimmon.Parser<{}> {
|
||||
return parsimmon.seq(operatorsParser, nextParser).or(nextParser);
|
||||
}
|
||||
|
||||
@@ -271,12 +270,12 @@ const lang = parsimmon.createLanguage({
|
||||
}
|
||||
});
|
||||
|
||||
export function parse(str) {
|
||||
export function parse(str): Expression {
|
||||
if (!str) return null;
|
||||
return lang.Expression.tryParse(str);
|
||||
}
|
||||
|
||||
export function stringify(exp, level = 0) {
|
||||
export function stringify(exp, level = 0): string {
|
||||
if (!Array.isArray(exp)) return JSON.stringify(exp);
|
||||
|
||||
const opLevels = {
|
||||
@@ -302,7 +301,7 @@ export function stringify(exp, level = 0) {
|
||||
|
||||
const op = exp[0].toUpperCase();
|
||||
|
||||
function wrap(e) {
|
||||
function wrap(e): string {
|
||||
if (opLevels[op] <= level) return `(${e})`;
|
||||
else return e;
|
||||
}
|
||||
@@ -360,7 +359,7 @@ export function stringify(exp, level = 0) {
|
||||
}
|
||||
}
|
||||
|
||||
export function parseLikePattern(pat, esc) {
|
||||
export function parseLikePattern(pat, esc): string[] {
|
||||
const chars = pat.split("");
|
||||
|
||||
for (let i = 0; i < chars.length; ++i) {
|
||||
@@ -1,15 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
import { map, parseLikePattern } from "./expression-parser";
|
||||
import { booleanCnf } from "./expression-cnf";
|
||||
import { naiveDpll } from "./sat-solver";
|
||||
import { Expression } from "../types";
|
||||
|
||||
const isArray = Array.isArray;
|
||||
|
||||
const regExpCache = new WeakMap();
|
||||
|
||||
const REDUCE_SKIP = {};
|
||||
function reduce(exp, callback) {
|
||||
function reduce(exp, callback): Expression {
|
||||
let loop = true;
|
||||
while (loop) {
|
||||
loop = false;
|
||||
@@ -26,8 +25,45 @@ function reduce(exp, callback) {
|
||||
return exp;
|
||||
}
|
||||
|
||||
export function evaluate(exp, obj, now, cb) {
|
||||
function getRegExp(pat, esc) {
|
||||
export function likePatternToRegExp(pat, esc = "", flags = ""): RegExp {
|
||||
const convChars = {
|
||||
"-": "\\-",
|
||||
"/": "\\/",
|
||||
"\\": "\\/",
|
||||
"^": "\\^",
|
||||
$: "\\$",
|
||||
"*": "\\*",
|
||||
"+": "\\+",
|
||||
"?": "\\?",
|
||||
".": "\\.",
|
||||
"(": "\\(",
|
||||
")": "\\)",
|
||||
"|": "\\|",
|
||||
"[": "\\[",
|
||||
"]": "\\]",
|
||||
"{": "\\{",
|
||||
"}": "\\}",
|
||||
"\\%": ".*",
|
||||
"\\_": "."
|
||||
};
|
||||
let chars = parseLikePattern(pat, esc);
|
||||
if (!chars.length) return new RegExp("^$", flags);
|
||||
chars = chars.map(c => convChars[c] || c);
|
||||
chars[0] = chars[0] === ".*" ? "" : "^" + chars[0];
|
||||
const l = chars.length - 1;
|
||||
chars[l] = [".*", ""].includes(chars[l]) ? "" : chars[l] + "$";
|
||||
return new RegExp(chars.join(""), flags);
|
||||
}
|
||||
|
||||
export function evaluate(
|
||||
exp,
|
||||
obj,
|
||||
now: number,
|
||||
cb?: Function
|
||||
): string | number | boolean | null;
|
||||
export function evaluate(exp, obj?, now?: number, cb?: Function): Expression;
|
||||
export function evaluate(exp, obj?, now?: number, cb?: Function): Expression {
|
||||
function getRegExp(pat, esc): RegExp {
|
||||
const k = `${esc || ""}:${pat}`;
|
||||
let c = regExpCache.get(exp);
|
||||
if (!c) regExpCache.set(exp, (c = {}));
|
||||
@@ -162,11 +198,11 @@ export function evaluate(exp, obj, now, cb) {
|
||||
});
|
||||
}
|
||||
|
||||
export function and(exp1, exp2) {
|
||||
export function and(exp1, exp2): Expression {
|
||||
if (!isArray(exp1)) return exp1 ? exp2 : exp1;
|
||||
if (!isArray(exp2)) return exp2 ? exp1 : exp2;
|
||||
|
||||
let res = ["AND"];
|
||||
let res: Expression = ["AND"];
|
||||
|
||||
if (exp1[0] === "AND") res = res.concat(exp1.slice(1));
|
||||
else res.push(exp1);
|
||||
@@ -177,11 +213,11 @@ export function and(exp1, exp2) {
|
||||
return res;
|
||||
}
|
||||
|
||||
export function or(exp1, exp2) {
|
||||
export function or(exp1, exp2): Expression {
|
||||
if (!isArray(exp1)) return exp1 ? exp1 : exp2;
|
||||
if (!isArray(exp2)) return exp2 ? exp2 : exp1;
|
||||
|
||||
let res = ["OR"];
|
||||
let res: Expression = ["OR"];
|
||||
|
||||
if (exp1[0] === "OR") res = res.concat(exp1.slice(1));
|
||||
else res.push(exp1);
|
||||
@@ -192,49 +228,19 @@ export function or(exp1, exp2) {
|
||||
return res;
|
||||
}
|
||||
|
||||
export function not(exp) {
|
||||
export function not(exp): Expression {
|
||||
if (isArray(exp) && exp[0] === "NOT") return exp[1];
|
||||
return ["NOT", exp];
|
||||
}
|
||||
|
||||
export function subset(exp1, exp2) {
|
||||
export function subset(exp1, exp2): boolean {
|
||||
const e = evaluate(["NOT", ["OR", ["NOT", exp1], exp2]]);
|
||||
if (!isArray(e)) return !e;
|
||||
const { vars, clauses } = booleanCnf(e);
|
||||
return !naiveDpll(clauses, vars);
|
||||
}
|
||||
|
||||
export function likePatternToRegExp(pat, esc = "", flags = "") {
|
||||
const convChars = {
|
||||
"-": "\\-",
|
||||
"/": "\\/",
|
||||
"\\": "\\/",
|
||||
"^": "\\^",
|
||||
$: "\\$",
|
||||
"*": "\\*",
|
||||
"+": "\\+",
|
||||
"?": "\\?",
|
||||
".": "\\.",
|
||||
"(": "\\(",
|
||||
")": "\\)",
|
||||
"|": "\\|",
|
||||
"[": "\\[",
|
||||
"]": "\\]",
|
||||
"{": "\\{",
|
||||
"}": "\\}",
|
||||
"\\%": ".*",
|
||||
"\\_": "."
|
||||
};
|
||||
let chars = parseLikePattern(pat, esc);
|
||||
if (!chars.length) return new RegExp("^$", flags);
|
||||
chars = chars.map(c => convChars[c] || c);
|
||||
chars[0] = chars[0] === ".*" ? "" : "^" + chars[0];
|
||||
const l = chars.length - 1;
|
||||
chars[l] = [".*", ""].includes(chars[l]) ? "" : chars[l] + "$";
|
||||
return new RegExp(chars.join(""), flags);
|
||||
}
|
||||
|
||||
export function extractParams(exp) {
|
||||
export function extractParams(exp): string[] {
|
||||
const params = new Set();
|
||||
map(exp, e => {
|
||||
if (isArray(e) && e[0] === "PARAM") params.add(e[1]);
|
||||
@@ -1,10 +1,8 @@
|
||||
"use strict";
|
||||
|
||||
let cache1 = new Map();
|
||||
let cache2 = new Map();
|
||||
const keys = new WeakMap();
|
||||
|
||||
function getKey(obj) {
|
||||
function getKey(obj): string {
|
||||
if (obj === null) return "null";
|
||||
else if (obj === undefined) return "undefined";
|
||||
|
||||
@@ -22,7 +20,7 @@ function getKey(obj) {
|
||||
return k;
|
||||
}
|
||||
|
||||
export default function memoize(func) {
|
||||
export default function memoize(func): Function {
|
||||
return (...args) => {
|
||||
const key = JSON.stringify(args.map(getKey)) + getKey(func);
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
* The code is very similar to the pure table solver, except we use a
|
||||
* unit-propagating, derived-varlist-returing satisfiableAt.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
function satisfiableAt(clauses, varVals, varnr, val) {
|
||||
/* eslint-disable @typescript-eslint/no-use-before-define */
|
||||
|
||||
function satisfiableAt(clauses, varVals, varnr, val): boolean {
|
||||
if (varnr !== 0) varVals[varnr] = val;
|
||||
|
||||
const propRes = unitPropagate(clauses, varVals);
|
||||
@@ -87,7 +88,7 @@ function satisfiableAt(clauses, varVals, varnr, val) {
|
||||
* undetermined. The values of variables on the list will be restored (set to
|
||||
* 0) during backtracking.
|
||||
*/
|
||||
function unitPropagate(clauses, varVals) {
|
||||
function unitPropagate(clauses, varVals): number[] | 1 | -1 {
|
||||
// all vars with a value derived in the forthcoming main loop
|
||||
const derivedVars = [];
|
||||
|
||||
@@ -167,7 +168,7 @@ function unitPropagate(clauses, varVals) {
|
||||
return derivedVars;
|
||||
}
|
||||
|
||||
export function naiveDpll(clauses, maxvarnr) {
|
||||
export function naiveDpll(clauses, maxvarnr): boolean {
|
||||
// variable values are 0 if not set, 1 if positive, -1 if negative
|
||||
const varVals = new Int32Array(maxvarnr + 1);
|
||||
return satisfiableAt(clauses, varVals, 0, 0);
|
||||
@@ -1,3 +0,0 @@
|
||||
export default function pass() {
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function pass(): number {
|
||||
return 1;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
export default function test(resourceType, resource) {
|
||||
export default function test(resourceType, resource): number {
|
||||
if (resourceType === "devices") {
|
||||
if (resource["Tags.test"] && resource["Tags.test"].value[0]) return 1;
|
||||
} else if (resourceType === "presets") {
|
||||
Reference in new issue
Block a user