sievelib-ts 0.0.0-stage → 0.1.1

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.
package/LICENSE ADDED
@@ -0,0 +1,3 @@
1
+ MIT License
2
+
3
+ Copyright (c) sievelib contributors
package/README.md CHANGED
@@ -1,3 +1,191 @@
1
- # Temporary Holding Version
1
+ # sievelib-ts
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Client-side Sieve and ManageSieve library for Node.js, written in TypeScript.
4
+
5
+ - [Sieve: An Email Filtering Language (RFC 5228)](https://datatracker.ietf.org/doc/html/rfc5228)
6
+ - [ManageSieve (RFC 5804)](https://datatracker.ietf.org/doc/html/rfc5804)
7
+
8
+ `sievelib-ts` has its own npm versioning. Its behavior is based on the sibling
9
+ Python [`sievelib`](https://github.com/tonioo/sievelib) source at commit
10
+ [`7adda3b6`](https://github.com/tonioo/sievelib/commit/7adda3b66690c1d3f2cb55ce14d186ab0715c77a), rather than on a matching PyPI version number.
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install sievelib-ts
16
+ ```
17
+
18
+ The package supports Node.js 20 or later and provides both ESM and CommonJS
19
+ exports.
20
+
21
+ ```js
22
+ // ESM
23
+ import { Parser, FiltersSet, Client } from "sievelib-ts";
24
+
25
+ // CommonJS
26
+ const { Parser, FiltersSet, Client } = require("sievelib-ts");
27
+ ```
28
+
29
+ ## Sieve tools
30
+
31
+ ### Supported syntax
32
+
33
+ The parser supports the Sieve features and extensions implemented by the
34
+ reference Python project, including Copy, Body, Vacation and
35
+ Vacation Seconds, Relational, Imap4flags, Regex, Notifications, Environment,
36
+ Date, and Variables. `encoded-character` syntax from RFC 5228 section 2.4.2.4
37
+ is intentionally not supported.
38
+
39
+ Extensions must be declared with `require` before their commands or match tags
40
+ are used. The parser validates this requirement.
41
+
42
+ ### Parse and inspect a script
43
+
44
+ ```js
45
+ import { Parser } from "sievelib-ts";
46
+
47
+ const parser = new Parser();
48
+
49
+ if (!parser.parse('require ["fileinto"];\nfileinto "Archive";')) {
50
+ console.error(parser.error);
51
+ } else {
52
+ console.log(parser.dump());
53
+ console.log(parser.toSieve());
54
+
55
+ // Commands form a mutable AST. `walk()` visits every nested node.
56
+ for (const command of parser.result.flatMap((root) => [...root.walk()])) {
57
+ console.log(command.name, command.type);
58
+ }
59
+ }
60
+ ```
61
+
62
+ `parse()` accepts a JavaScript string or `Uint8Array`, returns `true` or
63
+ `false`, and sets `error` and `errorPos` on failure. The parsed top-level
64
+ commands are available as `parser.result`.
65
+
66
+ ### Create filters
67
+
68
+ `FiltersSet` provides the high-level builder API. Header names that are not a
69
+ supported test name are treated as `header` tests.
70
+
71
+ ```js
72
+ import { FiltersSet } from "sievelib-ts";
73
+
74
+ const filters = new FiltersSet("mail-rules");
75
+ filters.addfilter(
76
+ "invoice forwarding",
77
+ [["Subject", ":contains", "Invoice"]],
78
+ [["redirect", "billing@example.com"]],
79
+ );
80
+
81
+ console.log(filters.toSieve());
82
+ // # Filter: invoice forwarding
83
+ // if anyof (header :contains "Subject" "Invoice") {
84
+ // redirect "billing@example.com";
85
+ // }
86
+ ```
87
+
88
+ The builder automatically adds extension requirements where necessary. It
89
+ supports `anyof` (the default) and `allof`, negated conditions such as
90
+ `:notcontains`, and explicit header conditions when a header name collides with
91
+ a test name:
92
+
93
+ ```js
94
+ filters.addfilter(
95
+ "header named size",
96
+ [["header", "size", ":is", "large"]],
97
+ [["fileinto", "Large"]],
98
+ );
99
+ ```
100
+
101
+ ### Update existing filters
102
+
103
+ Load a parsed script into a filter set to retain `# Filter:` and
104
+ `# Description:` metadata. Rules with no filter comment are named
105
+ `Unnamed rule 1`, `Unnamed rule 2`, and so on.
106
+
107
+ ```js
108
+ import { FiltersSet, Parser } from "sievelib-ts";
109
+
110
+ const parser = new Parser();
111
+ parser.parse(`require ["fileinto"];
112
+ if header :contains "Subject" "invoice" { fileinto "Invoices"; }
113
+ `);
114
+
115
+ const filters = new FiltersSet("mail-rules");
116
+ filters.fromParserResult(parser);
117
+
118
+ filters.updatefilter(
119
+ "Unnamed rule 1",
120
+ "invoices",
121
+ [["Subject", ":contains", "invoice"]],
122
+ [["fileinto", "Invoices"]],
123
+ );
124
+
125
+ console.log(filters.get_filter_conditions("invoices"));
126
+ console.log(filters.get_filter_actions("invoices"));
127
+ ```
128
+
129
+ Use `disablefilter`, `enablefilter`, `movefilter`, `removefilter`, and
130
+ `replacefilter` to manage a filter set. Disabled filters are represented as an
131
+ `if false` wrapper, rather than comments, so the original AST remains usable.
132
+
133
+ To use another filter-comment convention, configure the prefixes:
134
+
135
+ ```js
136
+ const roundcubeFilters = new FiltersSet("roundcube", "# rule:");
137
+ ```
138
+
139
+ ## ManageSieve tools
140
+
141
+ `Client` is an asynchronous Node.js ManageSieve client. It supports mandatory
142
+ protocol commands, `RENAMESCRIPT` (with a fallback for servers without the
143
+ extension), PLAIN, LOGIN, OAUTHBEARER, and XOAUTH2 authentication, STARTTLS,
144
+ and implicit TLS.
145
+
146
+ ```js
147
+ import { Client } from "sievelib-ts";
148
+
149
+ const client = new Client("mail.example.com", 4190);
150
+
151
+ try {
152
+ const authenticated = await client.connect(
153
+ "username",
154
+ "password",
155
+ "", // authorization identity
156
+ true, // request STARTTLS
157
+ false, // implicit TLS instead of STARTTLS
158
+ "PLAIN",
159
+ );
160
+
161
+ if (!authenticated) {
162
+ throw new Error(`${client.errcode}: ${client.errmsg}`);
163
+ }
164
+
165
+ const scripts = await client.listscripts();
166
+ console.log(scripts); // [activeScript, otherScripts]
167
+
168
+ await client.putscript("mail-rules", 'require ["fileinto"];\nkeep;\n');
169
+ await client.setactive("mail-rules");
170
+ console.log(await client.getscript("mail-rules"));
171
+ } finally {
172
+ if (client.authenticated) await client.logout();
173
+ else client.socket?.destroy();
174
+ }
175
+ ```
176
+
177
+ All network methods return promises. Failed protocol commands return `false` or
178
+ `null` as appropriate and populate `client.errcode` and `client.errmsg`.
179
+ Arguments containing carriage returns, line feeds, or NUL bytes are rejected to
180
+ prevent ManageSieve command injection.
181
+
182
+ ## Development and publishing
183
+
184
+ ```bash
185
+ npm run verify
186
+ npm pack --dry-run
187
+ ```
188
+
189
+ `npm run verify` runs type checking, tests, and the dual ESM/CommonJS build.
190
+ The `prepublishOnly` hook runs the same verification and validates the npm
191
+ tarball before publication.
@@ -0,0 +1,419 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/commands.ts
21
+ var commands_exports = {};
22
+ __export(commands_exports, {
23
+ ActionCommand: () => ActionCommand,
24
+ AddflagCommand: () => AddflagCommand,
25
+ AddressCommand: () => AddressCommand,
26
+ AllofCommand: () => AllofCommand,
27
+ AnyofCommand: () => AnyofCommand,
28
+ BadArgument: () => BadArgument,
29
+ BadValue: () => BadValue,
30
+ BodyCommand: () => BodyCommand,
31
+ Command: () => Command,
32
+ CommandError: () => CommandError,
33
+ ControlCommand: () => ControlCommand,
34
+ CurrentdateCommand: () => CurrentdateCommand,
35
+ DateCommand: () => DateCommand,
36
+ DiscardCommand: () => DiscardCommand,
37
+ ElseCommand: () => ElseCommand,
38
+ ElsifCommand: () => ElsifCommand,
39
+ EnvelopeCommand: () => EnvelopeCommand,
40
+ EnvironmentCommand: () => EnvironmentCommand,
41
+ ExistsCommand: () => ExistsCommand,
42
+ ExtensionNotLoaded: () => ExtensionNotLoaded,
43
+ FalseCommand: () => FalseCommand,
44
+ FileintoCommand: () => FileintoCommand,
45
+ HasflagCommand: () => HasflagCommand,
46
+ HeaderCommand: () => HeaderCommand,
47
+ IfCommand: () => IfCommand,
48
+ KeepCommand: () => KeepCommand,
49
+ NotCommand: () => NotCommand,
50
+ NotifyCommand: () => NotifyCommand,
51
+ RedirectCommand: () => RedirectCommand,
52
+ RejectCommand: () => RejectCommand,
53
+ RemoveflagCommand: () => RemoveflagCommand,
54
+ RequireCommand: () => RequireCommand,
55
+ SetCommand: () => SetCommand,
56
+ SetflagCommand: () => SetflagCommand,
57
+ SizeCommand: () => SizeCommand,
58
+ StopCommand: () => StopCommand,
59
+ TestCommand: () => TestCommand,
60
+ TrueCommand: () => TrueCommand,
61
+ UnknownCommand: () => UnknownCommand,
62
+ VacationCommand: () => VacationCommand,
63
+ addCommands: () => addCommands,
64
+ add_commands: () => add_commands,
65
+ getCommandInstance: () => getCommandInstance,
66
+ get_command_instance: () => get_command_instance,
67
+ loadedExtensions: () => loadedExtensions,
68
+ parser_state: () => parser_state,
69
+ resetExtensions: () => resetExtensions
70
+ });
71
+ module.exports = __toCommonJS(commands_exports);
72
+
73
+ // src/tools.ts
74
+ var quoted = /^"(?:[^"\\]|\\.)*"$/s;
75
+ var isQuoted = (value) => quoted.test(value);
76
+ var quote = (value) => `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
77
+ var unquote = (value) => isQuoted(value) ? value.slice(1, -1).replace(/\\(.)/gs, "$1") : value;
78
+ var unquoteArg = (value) => Array.isArray(value) ? value.map(unquote) : unquote(value);
79
+
80
+ // src/commands.ts
81
+ var CommandError = class extends Error {
82
+ };
83
+ var UnknownCommand = class extends CommandError {
84
+ constructor(name) {
85
+ super(`unknown command '${name}'`);
86
+ }
87
+ };
88
+ var BadArgument = class extends CommandError {
89
+ constructor(command, seen, expected) {
90
+ super(`bad argument ${String(seen)} for command ${command} (${String(expected)} expected)`);
91
+ }
92
+ };
93
+ var BadValue = class extends CommandError {
94
+ constructor(arg, value) {
95
+ super(`bad value ${String(value)} for argument ${arg}`);
96
+ }
97
+ };
98
+ var ExtensionNotLoaded = class extends CommandError {
99
+ constructor(name) {
100
+ super(`extension '${name}' not loaded`);
101
+ }
102
+ };
103
+ var loadedExtensions = [];
104
+ function resetExtensions() {
105
+ loadedExtensions = [];
106
+ }
107
+ var comparator = { name: "comparator", type: ["tag"], values: [":comparator"], extraArg: { type: ["string"], values: ['"i;octet"', '"i;ascii-casemap"'] } };
108
+ var addressPart = { name: "address-part", type: ["tag"], values: [":localpart", ":domain", ":all"] };
109
+ var matchType = { name: "match-type", type: ["tag"], values: [":is", ":contains", ":matches"], extensionValues: { ":count": "relational", ":value": "relational", ":regex": "regex" }, extraArg: { type: ["string"], values: ['"gt"', '"ge"', '"lt"', '"le"', '"eq"', '"ne"'], validFor: [":count", ":value"] } };
110
+ var Command = class _Command {
111
+ static argsDefinition = [];
112
+ static type = "action";
113
+ static acceptChildren = false;
114
+ static variableArgs = false;
115
+ static nonDeterministic = false;
116
+ static extension;
117
+ static mustFollow;
118
+ parent;
119
+ arguments = {};
120
+ extraArguments = {};
121
+ children = [];
122
+ hashComments = [];
123
+ nextArgPos = 0;
124
+ requiredArgs = -1;
125
+ requiredCount = 0;
126
+ curArg;
127
+ constructor(parent) {
128
+ this.parent = parent;
129
+ }
130
+ get name() {
131
+ return this.constructor.name.replace(/Command$/, "").toLowerCase();
132
+ }
133
+ get defs() {
134
+ return this.constructor.argsDefinition;
135
+ }
136
+ get type() {
137
+ return this.constructor.type;
138
+ }
139
+ get acceptChildren() {
140
+ return this.constructor.acceptChildren;
141
+ }
142
+ get variableArgs() {
143
+ return this.constructor.variableArgs;
144
+ }
145
+ get nonDeterministic() {
146
+ return this.constructor.nonDeterministic;
147
+ }
148
+ get extension() {
149
+ return this.constructor.extension;
150
+ }
151
+ get mustFollow() {
152
+ return this.constructor.mustFollow;
153
+ }
154
+ repr() {
155
+ return `${this.name} (type: ${this.type})`;
156
+ }
157
+ addchild(child) {
158
+ if (!this.acceptChildren) return false;
159
+ this.children.push(child);
160
+ return true;
161
+ }
162
+ completeCb() {
163
+ if (this.name === "require") {
164
+ const a = this.arguments.capabilities;
165
+ for (const v of Array.isArray(a) ? a : [a]) {
166
+ const x = unquote(v);
167
+ if (!loadedExtensions.includes(x)) loadedExtensions.push(x);
168
+ }
169
+ }
170
+ }
171
+ reassignArguments() {
172
+ const defs = this.defs;
173
+ if (this.nonDeterministic && defs.length === 2 && defs[0].name in this.arguments && !(defs[1].name in this.arguments)) {
174
+ this.arguments[defs[1].name] = this.arguments[defs[0].name];
175
+ delete this.arguments[defs[0].name];
176
+ this.requiredCount = 1;
177
+ }
178
+ }
179
+ iscomplete(type, value) {
180
+ if (this.variableArgs) return false;
181
+ if (this.requiredArgs < 0) this.requiredArgs = this.defs.filter((x) => x.required).length;
182
+ return (!this.curArg || !this.curArg.extraArg || this.curArg.extraArg.validFor && type && this.curArg.extraArg.type.includes(type) && !this.curArg.extraArg.validFor.includes(String(value))) && this.requiredCount === this.requiredArgs;
183
+ }
184
+ validValue(arg, value, check = true) {
185
+ const v = String(value).toLowerCase();
186
+ if (!arg.values && !arg.extensionValues) return true;
187
+ if (arg.values?.includes(v)) return true;
188
+ const e = arg.extensionValues?.[v];
189
+ if (e) {
190
+ if (check && !loadedExtensions.includes(e)) throw new ExtensionNotLoaded(e);
191
+ return true;
192
+ }
193
+ return false;
194
+ }
195
+ checkNextArg(type, value, add = true, checkExtension = true) {
196
+ if (!this.defs.length || this.iscomplete(type, value) && this.nextArgPos >= this.defs.length) return false;
197
+ if (this.curArg?.extraArg) {
198
+ const e = this.curArg.extraArg;
199
+ if (e.type.includes(type) && (!e.values || e.values.includes(String(value)))) {
200
+ if (add) this.extraArguments[this.curArg.name] = value;
201
+ this.curArg = void 0;
202
+ return true;
203
+ }
204
+ throw new BadValue(this.curArg.name, value);
205
+ }
206
+ for (let pos = this.nextArgPos; pos < this.defs.length; pos++) {
207
+ const arg = this.defs[pos];
208
+ if (arg.required) {
209
+ if (arg.type[0] === "testlist") {
210
+ if (type !== "test") throw new BadArgument(this.name, value, arg.type);
211
+ if (add) (this.arguments[arg.name] ??= []).push(value);
212
+ return true;
213
+ }
214
+ if (!arg.type.includes(type) && !(type === "string" && arg.type.includes("stringlist"))) throw new BadArgument(this.name, value, arg.type);
215
+ if (!this.validValue(arg, value, checkExtension)) throw new BadArgument(this.name, value, arg.type);
216
+ this.curArg = arg;
217
+ this.requiredCount++;
218
+ this.nextArgPos = pos + 1;
219
+ if (add) this.arguments[arg.name] = value;
220
+ return true;
221
+ }
222
+ if (arg.type.includes(type) && this.validValue(arg, value, checkExtension)) {
223
+ if (arg.extension && checkExtension && !loadedExtensions.includes(arg.extension)) throw new ExtensionNotLoaded(arg.extension);
224
+ if (arg.type.includes("tag") && arg.name in this.arguments) throw new BadArgument(this.name, value, `a single ${arg.name}`);
225
+ if (arg.extraArg && (!arg.extraArg.validFor || arg.extraArg.validFor.includes(String(value)))) this.curArg = arg;
226
+ if (add) this.arguments[arg.name] = value;
227
+ if (!arg.type.includes("tag")) this.nextArgPos = pos + 1;
228
+ return true;
229
+ }
230
+ }
231
+ return false;
232
+ }
233
+ argsAsTuple() {
234
+ const out = [this.name];
235
+ for (const d of this.defs) {
236
+ if (!(d.name in this.arguments)) continue;
237
+ let v = this.arguments[d.name];
238
+ let t = d.type;
239
+ if (t.includes("tag")) {
240
+ out.push(v);
241
+ if (!(d.name in this.extraArguments)) continue;
242
+ v = this.extraArguments[d.name];
243
+ t = d.extraArg.type;
244
+ }
245
+ out.push(t.includes("string") || t.includes("stringlist") ? unquoteArg(v) : v);
246
+ }
247
+ return out;
248
+ }
249
+ args_as_tuple() {
250
+ return this.argsAsTuple();
251
+ }
252
+ walk() {
253
+ const self = this;
254
+ return (function* () {
255
+ yield self;
256
+ for (const d of self.defs) {
257
+ const v = self.arguments[d.name];
258
+ if (Array.isArray(v) && d.type[0] === "testlist") for (const c of v) yield* c.walk();
259
+ else if (v instanceof _Command) yield* v.walk();
260
+ }
261
+ for (const c of self.children) yield* c.walk();
262
+ })();
263
+ }
264
+ toSieve(indent = 0) {
265
+ let s = `${" ".repeat(indent)}${this.name}`;
266
+ for (const d of this.defs) {
267
+ if (!(d.name in this.arguments)) continue;
268
+ let v = this.arguments[d.name];
269
+ let t = d.type;
270
+ if (t.includes("tag")) {
271
+ s += ` ${v}`;
272
+ if (!(d.name in this.extraArguments)) continue;
273
+ v = this.extraArguments[d.name];
274
+ t = d.extraArg.type;
275
+ }
276
+ s += " ";
277
+ if (Array.isArray(v)) {
278
+ s += d.type[0] === "testlist" ? `(${v.map((x) => x.toSieve(0).trim().replace(/;$/, "")).join(", ")})` : `[${v.map((x) => isQuoted(x) ? x : quote(x)).join(", ")}]`;
279
+ } else if (v instanceof _Command) s += v.toSieve(0).trim().replace(/;$/, "");
280
+ else s += t.includes("string") ? String(v) : String(v);
281
+ }
282
+ if (!this.acceptChildren) return this.type === "test" ? s : `${s};
283
+ `;
284
+ if (this.type !== "control") return s;
285
+ return `${s} {
286
+ ${this.children.map((c) => c.toSieve(indent + 4)).join("")}${" ".repeat(indent)}}
287
+ `;
288
+ }
289
+ tosieve(indent = 0) {
290
+ return this.toSieve(indent);
291
+ }
292
+ };
293
+ var ControlCommand = class extends Command {
294
+ static type = "control";
295
+ };
296
+ var ActionCommand = class extends Command {
297
+ static type = "action";
298
+ };
299
+ var TestCommand = class extends Command {
300
+ static type = "test";
301
+ };
302
+ function concrete(name, base, defs = [], options = {}) {
303
+ const C = class extends base {
304
+ static argsDefinition = defs;
305
+ static acceptChildren = options.acceptChildren ?? false;
306
+ static variableArgs = options.variableArgs ?? false;
307
+ static nonDeterministic = options.nonDeterministic ?? false;
308
+ static extension = options.extension;
309
+ static mustFollow = options.mustFollow;
310
+ };
311
+ Object.defineProperty(C, "name", { value: `${name[0].toUpperCase()}${name.slice(1)}Command` });
312
+ return C;
313
+ }
314
+ var RequireCommand = concrete("require", ControlCommand, [{ name: "capabilities", type: ["string", "stringlist"], required: true }]);
315
+ var IfCommand = concrete("if", ControlCommand, [{ name: "test", type: ["test"], required: true }], { acceptChildren: true });
316
+ var ElsifCommand = concrete("elsif", ControlCommand, [{ name: "test", type: ["test"], required: true }], { acceptChildren: true, mustFollow: ["if", "elsif"] });
317
+ var ElseCommand = concrete("else", ControlCommand, [], { acceptChildren: true, mustFollow: ["if", "elsif"] });
318
+ var StopCommand = concrete("stop", ActionCommand);
319
+ var FileintoCommand = concrete("fileinto", ActionCommand, [{ name: "copy", type: ["tag"], values: [":copy"], extension: "copy" }, { name: "create", type: ["tag"], values: [":create"], extension: "mailbox" }, { name: "flags", type: ["tag"], values: [":flags"], extraArg: { type: ["string", "stringlist"] }, extension: "imap4flags" }, { name: "mailbox", type: ["string"], required: true }], { extension: "fileinto" });
320
+ var RedirectCommand = concrete("redirect", ActionCommand, [{ name: "copy", type: ["tag"], values: [":copy"], extension: "copy" }, { name: "address", type: ["string"], required: true }]);
321
+ var RejectCommand = concrete("reject", ActionCommand, [{ name: "text", type: ["string"], required: true }], { extension: "reject" });
322
+ var KeepCommand = concrete("keep", ActionCommand, [{ name: "flags", type: ["tag"], values: [":flags"], extraArg: { type: ["string", "stringlist"] }, extension: "imap4flags" }]);
323
+ var DiscardCommand = concrete("discard", ActionCommand);
324
+ var flagDefs = [{ name: "variable-name", type: ["string"] }, { name: "list-of-flags", type: ["string", "stringlist"], required: true }];
325
+ var SetflagCommand = concrete("setflag", ActionCommand, flagDefs, { extension: "imap4flags", nonDeterministic: true });
326
+ var AddflagCommand = concrete("addflag", ActionCommand, flagDefs, { extension: "imap4flags", nonDeterministic: true });
327
+ var RemoveflagCommand = concrete("removeflag", ActionCommand, flagDefs, { extension: "imap4flags", nonDeterministic: true });
328
+ var NotifyCommand = concrete("notify", ActionCommand, [{ name: "from", type: ["tag"], values: [":from"], extraArg: { type: ["string"] } }, { name: "importance", type: ["tag"], values: [":importance"], extraArg: { type: ["string"] } }, { name: "options", type: ["tag"], values: [":options"], extraArg: { type: ["stringlist"] } }, { name: "message", type: ["tag"], values: [":message"], extraArg: { type: ["string"] } }, { name: "method", type: ["string"], required: true }], { extension: "enotify" });
329
+ var addr = [comparator, addressPart, matchType, { name: "header-list", type: ["string", "stringlist"], required: true }, { name: "key-list", type: ["string", "stringlist"], required: true }];
330
+ var AddressCommand = concrete("address", TestCommand, addr);
331
+ var EnvelopeCommand = concrete("envelope", TestCommand, addr, { extension: "envelope" });
332
+ var HeaderCommand = concrete("header", TestCommand, [comparator, matchType, { name: "header-names", type: ["string", "stringlist"], required: true }, { name: "key-list", type: ["string", "stringlist"], required: true }]);
333
+ var ExistsCommand = concrete("exists", TestCommand, [{ name: "header-names", type: ["string", "stringlist"], required: true }]);
334
+ var TrueCommand = concrete("true", TestCommand);
335
+ var FalseCommand = concrete("false", TestCommand);
336
+ var AllofCommand = concrete("allof", TestCommand, [{ name: "tests", type: ["testlist"], required: true }], { acceptChildren: true, variableArgs: true });
337
+ var AnyofCommand = concrete("anyof", TestCommand, [{ name: "tests", type: ["testlist"], required: true }], { acceptChildren: true, variableArgs: true });
338
+ var NotCommand = concrete("not", TestCommand, [{ name: "test", type: ["test"], required: true }], { acceptChildren: true });
339
+ var SizeCommand = concrete("size", TestCommand, [{ name: "comparator", type: ["tag"], values: [":over", ":under"], required: true }, { name: "limit", type: ["number"], required: true }]);
340
+ var BodyCommand = concrete("body", TestCommand, [comparator, matchType, { name: "body-transform", type: ["tag"], values: [":raw", ":content", ":text"], extraArg: { type: ["stringlist"], validFor: [":content"] } }, { name: "key-list", type: ["string", "stringlist"], required: true }], { extension: "body" });
341
+ var HasflagCommand = concrete("hasflag", TestCommand, [comparator, matchType, { name: "variable-list", type: ["string", "stringlist"] }, { name: "list-of-flags", type: ["string", "stringlist"], required: true }], { extension: "imap4flags", nonDeterministic: true });
342
+ var DateCommand = concrete("date", TestCommand, [{ name: "zone", type: ["tag"], values: [":zone", ":originalzone"], extraArg: { type: ["string"], validFor: [":zone"] } }, comparator, matchType, { name: "header-name", type: ["string"], required: true }, { name: "date-part", type: ["string"], required: true }, { name: "key-list", type: ["string", "stringlist"], required: true }], { extension: "date" });
343
+ var CurrentdateCommand = concrete("currentdate", TestCommand, [{ name: "zone", type: ["tag"], values: [":zone"], extraArg: { type: ["string"] } }, comparator, matchType, { name: "date-part", type: ["string"], required: true }, { name: "key-list", type: ["string", "stringlist"], required: true }], { extension: "date" });
344
+ var EnvironmentCommand = concrete("environment", TestCommand, [comparator, matchType, { name: "name", type: ["string"], required: true }, { name: "key-list", type: ["string", "stringlist"], required: true }], { extension: "environment" });
345
+ var VacationCommand = concrete("vacation", ActionCommand, [{ name: "subject", type: ["tag"], values: [":subject"], extraArg: { type: ["string"] } }, { name: "days", type: ["tag"], values: [":days"], extraArg: { type: ["number"] } }, { name: "seconds", type: ["tag"], extensionValues: { ":seconds": "vacation-seconds" }, extraArg: { type: ["number"] } }, { name: "from", type: ["tag"], values: [":from"], extraArg: { type: ["string"] } }, { name: "addresses", type: ["tag"], values: [":addresses"], extraArg: { type: ["string", "stringlist"] } }, { name: "handle", type: ["tag"], values: [":handle"], extraArg: { type: ["string"] } }, { name: "mime", type: ["tag"], values: [":mime"] }, { name: "reason", type: ["string"], required: true }], { extension: "vacation" });
346
+ var SetCommand = concrete("set", ActionCommand, [{ name: "case", type: ["tag"], values: [":lower", ":upper"] }, { name: "first-case", type: ["tag"], values: [":lowerfirst", ":upperfirst"] }, { name: "quotewildcard", type: ["tag"], values: [":quotewildcard"] }, { name: "encoding", type: ["tag"], extensionValues: { ":quoteregex": "regex", ":encodeurl": "enotify" } }, { name: "length", type: ["tag"], values: [":length"] }, { name: "name", type: ["string"], required: true }, { name: "value", type: ["string"], required: true }], { extension: "variables" });
347
+ var registry = /* @__PURE__ */ new Map();
348
+ for (const x of [RequireCommand, IfCommand, ElsifCommand, ElseCommand, StopCommand, FileintoCommand, RedirectCommand, RejectCommand, KeepCommand, DiscardCommand, SetflagCommand, AddflagCommand, RemoveflagCommand, NotifyCommand, AddressCommand, EnvelopeCommand, HeaderCommand, ExistsCommand, TrueCommand, FalseCommand, AllofCommand, AnyofCommand, NotCommand, SizeCommand, BodyCommand, HasflagCommand, DateCommand, CurrentdateCommand, EnvironmentCommand, VacationCommand, SetCommand]) registry.set(x.name.replace(/Command$/, "").toLowerCase(), x);
349
+ function addCommands(cmds) {
350
+ for (const c of Array.isArray(cmds) ? cmds : [cmds]) registry.set(c.name.replace(/Command$/, "").toLowerCase(), c);
351
+ }
352
+ function getCommandInstance(name, parent, checkexists = true) {
353
+ const C = registry.get(name.toLowerCase());
354
+ if (!C) throw new UnknownCommand(name);
355
+ const c = new C(parent);
356
+ if (checkexists && c.extension && !loadedExtensions.includes(c.extension)) throw new ExtensionNotLoaded(c.extension);
357
+ return c;
358
+ }
359
+ var parser_state = {
360
+ get loaded_extensions() {
361
+ return loadedExtensions;
362
+ },
363
+ set loaded_extensions(value) {
364
+ loadedExtensions = value;
365
+ }
366
+ };
367
+ var add_commands = addCommands;
368
+ var get_command_instance = getCommandInstance;
369
+ // Annotate the CommonJS export names for ESM import in node:
370
+ 0 && (module.exports = {
371
+ ActionCommand,
372
+ AddflagCommand,
373
+ AddressCommand,
374
+ AllofCommand,
375
+ AnyofCommand,
376
+ BadArgument,
377
+ BadValue,
378
+ BodyCommand,
379
+ Command,
380
+ CommandError,
381
+ ControlCommand,
382
+ CurrentdateCommand,
383
+ DateCommand,
384
+ DiscardCommand,
385
+ ElseCommand,
386
+ ElsifCommand,
387
+ EnvelopeCommand,
388
+ EnvironmentCommand,
389
+ ExistsCommand,
390
+ ExtensionNotLoaded,
391
+ FalseCommand,
392
+ FileintoCommand,
393
+ HasflagCommand,
394
+ HeaderCommand,
395
+ IfCommand,
396
+ KeepCommand,
397
+ NotCommand,
398
+ NotifyCommand,
399
+ RedirectCommand,
400
+ RejectCommand,
401
+ RemoveflagCommand,
402
+ RequireCommand,
403
+ SetCommand,
404
+ SetflagCommand,
405
+ SizeCommand,
406
+ StopCommand,
407
+ TestCommand,
408
+ TrueCommand,
409
+ UnknownCommand,
410
+ VacationCommand,
411
+ addCommands,
412
+ add_commands,
413
+ getCommandInstance,
414
+ get_command_instance,
415
+ loadedExtensions,
416
+ parser_state,
417
+ resetExtensions
418
+ });
419
+ //# sourceMappingURL=commands.cjs.map