feat(smart-scope): add rolling older-than date filters

Allow users to build bounded historical windows without hard-coded calendar dates. Keep the API additive and advertise support for independently released clients.

Closes #1751
This commit is contained in:
neon
2026-10-21 03:59:41 +00:00
parent 95fa2c77fb
commit d1cc18dc51
11 changed files with 165 additions and 13 deletions
+2
View File
@@ -37,6 +37,8 @@ export const SERVER_FEATURES = [
* names an author but no title.
*/
"metadata-search-results-per-provider",
/** Book and custom date filters accept the `moreThanAgo` rolling-boundary operator. */
"relative-date-ranges",
] as const;
export type ServerFeature = (typeof SERVER_FEATURES)[number];
+7 -5
View File
@@ -94,6 +94,7 @@ export type RuleOperator =
| "before"
| "after"
| "withinLast"
| "moreThanAgo"
| "isMissing"
| "isPresent"
| "isUnread"
@@ -117,13 +118,13 @@ export const FIELD_OPERATORS: Record<StaticRuleField, RuleOperator[]> = {
library: ["includesAny", "excludesAll"],
format: ["includesAny", "excludesAll"],
fileSize: ["eq", "notEq", "gt", "gte", "lt", "lte", "between", "isEmpty", "isNotEmpty"],
publishedDate: ["before", "after", "between", "withinLast", "isEmpty", "isNotEmpty"],
publishedDate: ["before", "after", "between", "withinLast", "moreThanAgo", "isEmpty", "isNotEmpty"],
publishedYear: ["eq", "notEq", "gt", "gte", "lt", "lte", "between", "isEmpty", "isNotEmpty"],
seriesIndex: ["eq", "notEq", "gt", "gte", "lt", "lte", "between", "isEmpty", "isNotEmpty"],
pageCount: ["gt", "gte", "lt", "lte", "between", "isEmpty", "isNotEmpty"],
addedAt: ["before", "after", "between", "withinLast"],
startedAt: ["before", "after", "between", "withinLast", "isEmpty", "isNotEmpty"],
finishedAt: ["before", "after", "between", "withinLast", "isEmpty", "isNotEmpty"],
addedAt: ["before", "after", "between", "withinLast", "moreThanAgo"],
startedAt: ["before", "after", "between", "withinLast", "moreThanAgo", "isEmpty", "isNotEmpty"],
finishedAt: ["before", "after", "between", "withinLast", "moreThanAgo", "isEmpty", "isNotEmpty"],
fileAvailability: ["isMissing", "isPresent"],
rating: ["eq", "gt", "gte", "lt", "lte", "isEmpty", "isNotEmpty"],
communityRating: ["eq", "notEq", "gt", "gte", "lt", "lte", "between", "isEmpty", "isNotEmpty"],
@@ -152,7 +153,7 @@ export const CUSTOM_FIELD_TYPE_OPERATORS: Record<CustomMetadataFieldType, RuleOp
text: ["contains", "notContains", "startsWith", "endsWith", "eq", "notEq", "isEmpty", "isNotEmpty"],
url: ["contains", "notContains", "startsWith", "endsWith", "eq", "notEq", "isEmpty", "isNotEmpty"],
number: ["eq", "notEq", "gt", "gte", "lt", "lte", "between", "isEmpty", "isNotEmpty"],
date: ["before", "after", "between", "withinLast", "isEmpty", "isNotEmpty"],
date: ["before", "after", "between", "withinLast", "moreThanAgo", "isEmpty", "isNotEmpty"],
boolean: ["isTrue", "isFalse", "isEmpty", "isNotEmpty"],
};
@@ -183,6 +184,7 @@ export const RULE_OPERATORS: RuleOperator[] = [
"before",
"after",
"withinLast",
"moreThanAgo",
"isMissing",
"isPresent",
"isUnread",