@noartem/xews 1.0.0 → 1.1.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.
Files changed (3) hide show
  1. package/README.md +16 -0
  2. package/index.js +67 -0
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  It is useful when you already have EWS access and want a simple way to upload, list, download, and clear files through Exchange without opening a mail client.
6
6
 
7
+ It can also synchronize one local folder through a dedicated draft-based channel. Sync sends changes as compressed batch attachments and removes each package after it is applied.
8
+
7
9
  ## Install
8
10
 
9
11
  Install from a local checkout:
@@ -58,6 +60,7 @@ Typical file transfer flow:
58
60
  xews upload --file ./archive.zip
59
61
  xews ls
60
62
  xews download
63
+ xews sync --dir ./data --channel team-a
61
64
  ```
62
65
 
63
66
  Show help:
@@ -97,6 +100,18 @@ Clear all attachments from the first draft:
97
100
  xews clear
98
101
  ```
99
102
 
103
+ Synchronize one local folder through a dedicated draft subject:
104
+
105
+ ```bash
106
+ xews sync --dir ./tmp/a --channel local-test
107
+ ```
108
+
109
+ Run a single sync cycle and exit:
110
+
111
+ ```bash
112
+ xews sync --dir ./tmp/a --channel local-test --once
113
+ ```
114
+
100
115
  ## Commands
101
116
 
102
117
  - `init`: create `~/.config/xews/auth.json` if needed and open it in your default editor
@@ -104,6 +119,7 @@ xews clear
104
119
  - `download`: download all file attachments from the first draft into the current directory
105
120
  - `list` / `ls`: show attachment names and sizes from the first draft
106
121
  - `clear`: remove all attachments from the first draft
122
+ - `sync`: synchronize one folder through a dedicated draft channel like `xews-sync:<channel>`
107
123
 
108
124
  ## Publish
109
125
 
package/index.js CHANGED
@@ -18,9 +18,12 @@ import {
18
18
  FileAttachment,
19
19
  DeleteMode,
20
20
  MessageBody,
21
+ SearchFilter,
22
+ ItemSchema,
21
23
  } from "ews-javascript-api";
22
24
 
23
25
  import { Cli, z } from "incur";
26
+ import { runSync } from "./sync.js";
24
27
 
25
28
  const CLI_VERSION = "1.0.0";
26
29
  const CONFIG_PATH = path.join(os.homedir(), ".config", "xews", "auth.json");
@@ -129,6 +132,36 @@ async function getFirstDraft(service) {
129
132
  return draft;
130
133
  }
131
134
 
135
+ async function getDraftBySubject(service, subject) {
136
+ const filter = new SearchFilter.IsEqualTo(ItemSchema.Subject, subject);
137
+ const res = await service.FindItems(
138
+ WellKnownFolderName.Drafts,
139
+ filter,
140
+ new ItemView(1),
141
+ );
142
+
143
+ if (res.Items.length === 0) return null;
144
+
145
+ const draft = res.Items[0];
146
+ await draft.Load(new PropertySet(BasePropertySet.FirstClassProperties));
147
+ return draft;
148
+ }
149
+
150
+ async function getOrCreateDraftBySubject(service, subject) {
151
+ const draft = await getDraftBySubject(service, subject);
152
+
153
+ if (draft) return draft;
154
+
155
+ const created = new EmailMessage(service);
156
+ created.Subject = subject;
157
+ created.Body = new MessageBody(`Generated for ${subject}`);
158
+
159
+ await created.Save(WellKnownFolderName.Drafts);
160
+ await created.Load(new PropertySet(BasePropertySet.FirstClassProperties));
161
+
162
+ return created;
163
+ }
164
+
132
165
  /* ================= ENTRY ================= */
133
166
 
134
167
  const cli = Cli.create("xews", {
@@ -274,4 +307,38 @@ cli.command("clear", {
274
307
  },
275
308
  });
276
309
 
310
+ cli.command("sync", {
311
+ description: "Synchronize one folder through an EWS draft channel",
312
+ options: z.object({
313
+ dir: z.string().describe("Folder to synchronize"),
314
+ channel: z
315
+ .string()
316
+ .default("default")
317
+ .describe("Sync channel name stored in a dedicated draft subject"),
318
+ interval: z
319
+ .number()
320
+ .int()
321
+ .positive()
322
+ .default(2000)
323
+ .describe("Polling interval in milliseconds"),
324
+ once: z.boolean().default(false).describe("Run one sync cycle and exit"),
325
+ }),
326
+ alias: {
327
+ dir: "d",
328
+ channel: "c",
329
+ },
330
+ async run(c) {
331
+ const service = createService();
332
+
333
+ await runSync({
334
+ service,
335
+ rootDir: c.options.dir,
336
+ channel: c.options.channel,
337
+ intervalMs: c.options.interval,
338
+ once: c.options.once,
339
+ getOrCreateDraftBySubject,
340
+ });
341
+ },
342
+ });
343
+
277
344
  await cli.serve();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noartem/xews",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "CLI for transferring files through Exchange drafts over EWS",
5
5
  "main": "index.js",
6
6
  "scripts": {