(tokens)
| 222 | for (let i = 1; i < arr.length; i++) { |
| 223 | const a = arr[i - 1]; |
| 224 | const b = arr[i]; |
| 225 | if (a.t === "op" && b.t === "op" && a.op === b.op) { |
| 226 | arr.splice(i, 1); |
| 227 | i--; |
| 228 | } |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | function normalizeTokensForIpc(tokens) { |
| 233 | const out = tokens.filter((tok) => { |
| 234 | if (tok.t === "clause") return !!String(tok.value || "").trim(); |
| 235 | if (tok.t === "op") return tok.op === "AND" || tok.op === "OR"; |
| 236 | if (tok.t === "not") return true; |
| 237 | if (tok.t === "filter") { |
| 238 | const k = String(tok.kind || "").trim(); |
| 239 | if (!k || !["designer", "license", "parentModel", "tag", "fileType", "printed", "isNew", "favorite", "rating", "ratingMin"].includes(k)) return false; |
| 240 | const v = tok.value != null ? String(tok.value).trim() : ""; |
| 241 | if (k === "printed") return v === "printed" || v === "not-printed" || v === "unprinted" || v === "want" || v === "queued" || v === "printing" || v === "failed" || v === "ever-printed" || v === "never-printed"; |
| 242 | if (k === "isNew") return v === "new" || v === "not-new"; |
| 243 | if (k === "favorite") return v === "favorited" || v === "not-favorited"; |
| 244 | if (k === "rating") return v === "unrated" || /^[1-5]$/.test(v); |
| 245 | if (k === "ratingMin") return /^[1-5]$/.test(v); |
| 246 | return !!v; |
| 247 | } |
| 248 | if (tok.t === "filterMulti") { |
| 249 | const k = String(tok.kind || "").trim(); |
| 250 | if (!["designer", "license", "parentModel", "tag"].includes(k)) return false; |
| 251 | return Array.isArray(tok.values) && tok.values.some((x) => String(x).trim()); |
no test coverage detected