@doist/todoist-sdk 15.1.0 → 15.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,8 @@ export * from './sanitization.js';
3
3
  export { getTaskUrl, getProjectUrl, getSectionUrl, getLabelUrl, getFilterUrl, getWorkspaceFilterUrl, getWorkspaceUrl, getTaskCommentsUrl, getProjectCommentsUrl, getTaskCommentUrl, getProjectCommentUrl, parseTodoistUrl, TODOIST_LINK_TYPES, } from './url-helpers.js';
4
4
  export { createCommand } from './sync-helpers.js';
5
5
  export * from './project-helpers.js';
6
+ export { isDateDrivenQuery } from './filter-queries.js';
7
+ export { FILTER_QUERY_LANGUAGES } from './filter-query-definitions.js';
6
8
  export * from './task-sorting.js';
7
9
  export * from './view-options.js';
8
10
  export { parseWebhookPayload, verifyWebhookSignature } from './webhook-parser.js';
@@ -110,3 +110,22 @@ export function getSanitizedContent(input) {
110
110
  export function getSanitizedTasks(tasks) {
111
111
  return tasks.map((task) => (Object.assign(Object.assign({}, task), { sanitizedContent: getSanitizedContent(task.content) })));
112
112
  }
113
+ /**
114
+ * Escapes text for use as a token in a Todoist filter query.
115
+ *
116
+ * Prefixes parentheses, pipes, ampersands, exclamation marks, commas, and
117
+ * backslashes with a backslash. Quotes and asterisks are left unchanged.
118
+ * Whitespace validation and trimming are the caller's responsibility.
119
+ * Pass unescaped text to this function once, before inserting it into a query.
120
+ *
121
+ * @example
122
+ * const tasks = await api.getTasksByFilter({
123
+ * query: `search: ${escapeFilterToken(searchText)}`,
124
+ * })
125
+ *
126
+ * @param text - The text to escape, not a complete filter query
127
+ * @returns The escaped text, with whitespace preserved
128
+ */
129
+ export function escapeFilterToken(text) {
130
+ return text.replace(/[()|&!,\\]/g, '\\$&');
131
+ }
@@ -249,6 +249,8 @@ function defaultSortOrder(sortedBy) {
249
249
  *
250
250
  * This function sorts only the tasks supplied to it. Fetch every page first
251
251
  * when globally correct ordering is required for a paginated query.
252
+ *
253
+ * @see isDateDrivenQuery
252
254
  */
253
255
  export function sortTasks(tasks, options, context = {}) {
254
256
  var _a, _b, _c;
@@ -0,0 +1,42 @@
1
+ export type IsDateDrivenQueryOptions = {
2
+ /**
3
+ * Language the query is written in, as the account's `lang`. Unknown codes
4
+ * and regional variants fall back to their base language, then to English.
5
+ */
6
+ lang?: string | null;
7
+ };
8
+ /**
9
+ * Returns whether a filter query leads with dates rather than with priority.
10
+ *
11
+ * Todoist gives every filter one of two default hierarchies, and a query that
12
+ * mentions dates gets the date-first one. Pass the answer to {@link sortTasks}
13
+ * as its `defaultOrder`:
14
+ *
15
+ * ```ts
16
+ * sortTasks(tasks, {
17
+ * sortedBy: savedOptions?.sortedBy,
18
+ * sortOrder: savedOptions?.sortOrder,
19
+ * defaultOrder: isDateDrivenQuery(query, { lang }) ? 'DATE_FIRST' : 'PRIORITY_FIRST',
20
+ * })
21
+ * ```
22
+ *
23
+ * The query is read in `lang`, the account language `getUser` returns, the way
24
+ * the API reads it. English keywords parse under every language, so `lang` only
25
+ * matters for a query written in the account's own: `sin fecha` is a keyword
26
+ * under `es` and an unreadable phrase under `en`.
27
+ *
28
+ * Commas divide a filter into subqueries and Todoist draws each one as its own
29
+ * list, with its own hierarchy. Classify them one at a time for exact results;
30
+ * a whole query answers true only when every subquery is date-driven.
31
+ *
32
+ * Two kinds of unreadable query part company here. One that breaks the grammar,
33
+ * such as `(today`, returns false. One that only breaks the vocabulary, such as
34
+ * `sin fecha` under `en`, returns true, because a word this package doesn't
35
+ * recognize is treated as a bare date. Pass the account's own `lang` and the
36
+ * question doesn't arise; get it wrong and a keyword can read as a date.
37
+ *
38
+ * The function performs no I/O.
39
+ *
40
+ * @see sortTasks
41
+ */
42
+ export declare function isDateDrivenQuery(query: string, options?: IsDateDrivenQueryOptions): boolean;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Languages the Todoist filter parser reads queries in.
3
+ *
4
+ * English keywords parse under every one of them, so only a query written in
5
+ * the account's own language needs the matching code.
6
+ */
7
+ export declare const FILTER_QUERY_LANGUAGES: readonly ["cs", "da", "de", "en", "es", "fi", "fr", "it", "ja", "ko", "nb", "nl", "pl", "pt", "ru", "sv", "tr", "zh_cn", "zh_tw"];
8
+ /** A language the Todoist filter parser reads queries in. */
9
+ export type FilterQueryLanguage = (typeof FILTER_QUERY_LANGUAGES)[number];
10
+ /** Token kinds the Todoist filter lexer recognizes, in the order it tries them. */
11
+ export declare const FILTER_QUERY_TOKENS: readonly ["LEFT", "RIGHT", "NOT", "AND", "OR", "PRIORITY", "NO_PRIORITY", "PROJECT", "SECTION", "PROJECT_SINGLE", "LABEL", "NO_LABELS", "DUE", "DUE_BEFORE", "DUE_AFTER", "DEADLINE", "DEADLINE_BEFORE", "DEADLINE_AFTER", "DATE", "DATE_BEFORE", "DATE_AFTER", "DUE_RECURRING", "OVERDUE", "WITHIN_DAYS", "NO_DUE_DATE", "NO_DEADLINE", "NO_DATE", "NO_TIME", "CREATED", "CREATED_BEFORE", "CREATED_AFTER", "ADDED_BY", "SHARED", "ASSIGNED", "TO_ME", "TO_OTHERS", "ASSIGNED_TO", "ASSIGNED_BY", "SUBTASK", "VIEW_ALL", "ALL", "SEARCH", "WORKSPACE_SINGLE", "UNCOMPLETABLE"];
12
+ /** A token kind the Todoist filter lexer recognizes. */
13
+ export type FilterQueryToken = (typeof FILTER_QUERY_TOKENS)[number];
14
+ /**
15
+ * Tokens that make a subquery date-driven, from FilteristKt's `Grouper`.
16
+ *
17
+ * `recurring`, `no date` and `created:` are absent there too. Each says
18
+ * something about dates without giving the list one to lead with.
19
+ */
20
+ export declare const FILTER_QUERY_DATE_TOKENS: readonly ["DUE", "DUE_BEFORE", "DUE_AFTER", "DEADLINE", "DEADLINE_BEFORE", "DEADLINE_AFTER", "DATE", "DATE_BEFORE", "DATE_AFTER", "OVERDUE", "WITHIN_DAYS"];
21
+ /** Tokens that join or negate operands rather than standing as one. */
22
+ export declare const FILTER_QUERY_OPERATOR_TOKENS: readonly ["LEFT", "RIGHT", "NOT", "AND", "OR"];
23
+ /**
24
+ * Localized patterns per token, each one the body of `^\s*<pattern>\s*` and
25
+ * matched case-insensitively at the cursor.
26
+ */
27
+ export declare const FILTER_QUERY_PATTERNS: Record<FilterQueryLanguage, Record<FilterQueryToken, readonly string[]>>;
@@ -4,6 +4,10 @@ export { getTaskUrl, getProjectUrl, getSectionUrl, getLabelUrl, getFilterUrl, ge
4
4
  export type { TodoistLinkType, TodoistUrlInfo } from './url-helpers.js';
5
5
  export { createCommand } from './sync-helpers.js';
6
6
  export * from './project-helpers.js';
7
+ export { isDateDrivenQuery } from './filter-queries.js';
8
+ export type { IsDateDrivenQueryOptions } from './filter-queries.js';
9
+ export { FILTER_QUERY_LANGUAGES } from './filter-query-definitions.js';
10
+ export type { FilterQueryLanguage } from './filter-query-definitions.js';
7
11
  export * from './task-sorting.js';
8
12
  export * from './view-options.js';
9
13
  export { parseWebhookPayload, verifyWebhookSignature } from './webhook-parser.js';
@@ -33,3 +33,20 @@ export declare function getSanitizedContent(input: string): string;
33
33
  * @returns Array of tasks with added sanitizedContent property
34
34
  */
35
35
  export declare function getSanitizedTasks(tasks: Task[]): TaskWithSanitizedContent[];
36
+ /**
37
+ * Escapes text for use as a token in a Todoist filter query.
38
+ *
39
+ * Prefixes parentheses, pipes, ampersands, exclamation marks, commas, and
40
+ * backslashes with a backslash. Quotes and asterisks are left unchanged.
41
+ * Whitespace validation and trimming are the caller's responsibility.
42
+ * Pass unescaped text to this function once, before inserting it into a query.
43
+ *
44
+ * @example
45
+ * const tasks = await api.getTasksByFilter({
46
+ * query: `search: ${escapeFilterToken(searchText)}`,
47
+ * })
48
+ *
49
+ * @param text - The text to escape, not a complete filter query
50
+ * @returns The escaped text, with whitespace preserved
51
+ */
52
+ export declare function escapeFilterToken(text: string): string;
@@ -9,7 +9,10 @@ export type TaskSortOptions = {
9
9
  sortedBy?: SortedBy | null;
10
10
  /** Saved or requested direction. Defaults by field when absent. */
11
11
  sortOrder?: SortOrder | null;
12
- /** Default hierarchy for this view or filter query. */
12
+ /**
13
+ * Default hierarchy for this view or filter query. Today and Upcoming are
14
+ * date-first; for a filter, {@link isDateDrivenQuery} reads it off the query.
15
+ */
13
16
  defaultOrder: DefaultTaskOrder;
14
17
  };
15
18
  export type TaskSortContext = {
@@ -34,5 +37,7 @@ export type TaskSortContext = {
34
37
  *
35
38
  * This function sorts only the tasks supplied to it. Fetch every page first
36
39
  * when globally correct ordering is required for a paginated query.
40
+ *
41
+ * @see isDateDrivenQuery
37
42
  */
38
43
  export declare function sortTasks(tasks: readonly Task[], options: TaskSortOptions, context?: TaskSortContext): Task[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doist/todoist-sdk",
3
- "version": "15.1.0",
3
+ "version": "15.3.0",
4
4
  "description": "The official TypeScript SDK for the Todoist REST API.",
5
5
  "author": "Doist developers",
6
6
  "repository": "https://github.com/Doist/todoist-sdk-typescript",
@@ -72,7 +72,7 @@
72
72
  "ts-node": "10.9.2",
73
73
  "type-fest": "^5.0.0",
74
74
  "typescript": "5.9.3",
75
- "vitest": "4.1.0"
75
+ "vitest": "4.1.11"
76
76
  },
77
77
  "peerDependencies": {
78
78
  "type-fest": "^4.12.0 || ^5.5.0"