@saptools/sharepoint-excel 0.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.
- package/README.md +352 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +1365 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +271 -0
- package/dist/index.js +1179 -0
- package/dist/index.js.map +1 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# ๐ `@saptools/sharepoint-excel`
|
|
4
|
+
|
|
5
|
+
**Safe SharePoint Excel automation for Microsoft Graph app-only integrations.**
|
|
6
|
+
|
|
7
|
+
Create `.xlsx` files, read workbook content, append records, update cells, and add sheets from a focused CLI or typed TypeScript API without overwriting somebody else's SharePoint file by accident.
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/@saptools/sharepoint-excel)
|
|
10
|
+
[](https://nodejs.org)
|
|
11
|
+
[](https://packagephobia.com/result?p=@saptools/sharepoint-excel)
|
|
12
|
+
[](https://www.typescriptlang.org)
|
|
13
|
+
|
|
14
|
+
[Install](#-install) โข [Quick Start](#-quick-start) โข [CLI](#-cli) โข [Security](#-credential-security) โข [API](#-programmatic-usage)
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## โจ Features
|
|
21
|
+
|
|
22
|
+
- ๐ **Safe local profiles**: stores profile metadata under `~/.saptools/sharepoint-excel/` and keeps `clientSecret` in the OS credential vault by default
|
|
23
|
+
- ๐งญ **Graph app-only flow**: uses Azure AD client credentials to resolve SharePoint sites and document libraries
|
|
24
|
+
- ๐งฑ **Local workbook engine**: edits `.xlsx` bytes with `exceljs`, avoiding delegated-only Microsoft Graph workbook endpoints
|
|
25
|
+
- ๐ก๏ธ **No accidental overwrite on create**: refuses to create when the target path already exists
|
|
26
|
+
- ๐ **ETag-protected updates**: append/update/add-sheet operations upload with `If-Match` so concurrent SharePoint edits fail fast instead of being silently overwritten
|
|
27
|
+
- ๐ **Workbook reads**: inspect all sheets, a single sheet, or an A1 range
|
|
28
|
+
- โ **Content mutation**: append JSON objects/rows, update a single cell, and add new sheets with headers
|
|
29
|
+
- ๐งช **Fake-backed e2e tests**: package tests do not call real Microsoft Graph or SharePoint
|
|
30
|
+
- ๐งฐ **CLI and typed API**: every CLI action is backed by exported TypeScript functions
|
|
31
|
+
|
|
32
|
+
> [!NOTE]
|
|
33
|
+
> Microsoft Graph's direct Excel workbook APIs are excellent for delegated user flows, but several workbook mutation endpoints do not support application permissions. This package intentionally treats SharePoint as file storage, edits the workbook locally, and uploads the changed `.xlsx` with conflict protection.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## ๐ฆ Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Global CLI
|
|
41
|
+
npm install -g @saptools/sharepoint-excel
|
|
42
|
+
|
|
43
|
+
# Or as a dependency
|
|
44
|
+
npm install @saptools/sharepoint-excel
|
|
45
|
+
# pnpm add @saptools/sharepoint-excel
|
|
46
|
+
# yarn add @saptools/sharepoint-excel
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Requires **Node.js >= 20**. The CLI binary is `saptools-sharepoint-excel`.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## ๐ Quick Start
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# 1. Store an app-only SharePoint profile
|
|
57
|
+
saptools-sharepoint-excel config set \
|
|
58
|
+
--tenant "11111111-1111-1111-1111-111111111111" \
|
|
59
|
+
--client-id "22222222-2222-2222-2222-222222222222" \
|
|
60
|
+
--client-secret "<your-client-secret>" \
|
|
61
|
+
--site "contoso.sharepoint.com/sites/demo" \
|
|
62
|
+
--drive "Documents"
|
|
63
|
+
|
|
64
|
+
# 2. Prove auth and target resolution
|
|
65
|
+
saptools-sharepoint-excel test
|
|
66
|
+
|
|
67
|
+
# 3. Create a workbook without overwriting an existing file
|
|
68
|
+
saptools-sharepoint-excel create \
|
|
69
|
+
--path "Reports/orders.xlsx" \
|
|
70
|
+
--sheet "Orders" \
|
|
71
|
+
--headers "Name,Amount,Status" \
|
|
72
|
+
--rows '[{"Name":"Coffee","Amount":3,"Status":"open"}]'
|
|
73
|
+
|
|
74
|
+
# 4. Append one object by matching row 1 headers
|
|
75
|
+
saptools-sharepoint-excel append \
|
|
76
|
+
--path "Reports/orders.xlsx" \
|
|
77
|
+
--sheet "Orders" \
|
|
78
|
+
--record '{"Name":"Tea","Amount":8,"Status":"open"}'
|
|
79
|
+
|
|
80
|
+
# 5. Update one cell
|
|
81
|
+
saptools-sharepoint-excel update-cell \
|
|
82
|
+
--path "Reports/orders.xlsx" \
|
|
83
|
+
--sheet "Orders" \
|
|
84
|
+
--cell "C2" \
|
|
85
|
+
--value '"closed"'
|
|
86
|
+
|
|
87
|
+
# 6. Read workbook JSON
|
|
88
|
+
saptools-sharepoint-excel read --path "Reports/orders.xlsx" --json
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
For CI, every command can also read credentials from environment variables:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
export SHAREPOINT_EXCEL_TENANT_ID="11111111-1111-1111-1111-111111111111"
|
|
95
|
+
export SHAREPOINT_EXCEL_CLIENT_ID="22222222-2222-2222-2222-222222222222"
|
|
96
|
+
export SHAREPOINT_EXCEL_CLIENT_SECRET="<your-client-secret>"
|
|
97
|
+
export SHAREPOINT_EXCEL_SITE="contoso.sharepoint.com/sites/demo"
|
|
98
|
+
export SHAREPOINT_EXCEL_DRIVE="Documents"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The CLI also accepts the shorter `SHAREPOINT_TENANT_ID`, `SHAREPOINT_CLIENT_ID`, `SHAREPOINT_CLIENT_SECRET`, `SHAREPOINT_SITE`, and `SHAREPOINT_DRIVE` fallbacks for consistency with `@saptools/sharepoint-check`.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## ๐งฐ CLI
|
|
106
|
+
|
|
107
|
+
### Common auth flags
|
|
108
|
+
|
|
109
|
+
| Flag | Env | Description |
|
|
110
|
+
| --- | --- | --- |
|
|
111
|
+
| `--profile <name>` | `SHAREPOINT_EXCEL_PROFILE` | Stored profile name; default is `default` |
|
|
112
|
+
| `--tenant <id>` | `SHAREPOINT_EXCEL_TENANT_ID` | Azure AD tenant id |
|
|
113
|
+
| `--client-id <id>` | `SHAREPOINT_EXCEL_CLIENT_ID` | App registration client id |
|
|
114
|
+
| `--client-secret <secret>` | `SHAREPOINT_EXCEL_CLIENT_SECRET` | App registration client secret |
|
|
115
|
+
| `--site <ref>` | `SHAREPOINT_EXCEL_SITE` | SharePoint site, e.g. `contoso.sharepoint.com/sites/demo` |
|
|
116
|
+
| `--drive <nameOrId>` | `SHAREPOINT_EXCEL_DRIVE` | Document library name or id |
|
|
117
|
+
| `--json` | - | Machine-readable output |
|
|
118
|
+
|
|
119
|
+
### ๐ `config set`
|
|
120
|
+
|
|
121
|
+
Store a reusable local profile.
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
saptools-sharepoint-excel config set \
|
|
125
|
+
--profile finance \
|
|
126
|
+
--tenant "$SHAREPOINT_EXCEL_TENANT_ID" \
|
|
127
|
+
--client-id "$SHAREPOINT_EXCEL_CLIENT_ID" \
|
|
128
|
+
--client-secret "$SHAREPOINT_EXCEL_CLIENT_SECRET" \
|
|
129
|
+
--site "contoso.sharepoint.com/sites/finance" \
|
|
130
|
+
--drive "Documents"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
By default, the secret goes to the OS credential vault:
|
|
134
|
+
|
|
135
|
+
- macOS: Keychain
|
|
136
|
+
- Windows: Credential Manager
|
|
137
|
+
- Linux: Secret Service-compatible keyring
|
|
138
|
+
|
|
139
|
+
For headless CI containers where an OS keyring is unavailable, an explicit plaintext fallback exists:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
SAPTOOLS_SHAREPOINT_EXCEL_ALLOW_PLAINTEXT=1 \
|
|
143
|
+
saptools-sharepoint-excel config set --store file --allow-plaintext-secret ...
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Use that only in controlled CI environments. The file is written with `0600` permissions under `~/.saptools/sharepoint-excel/secrets.json`.
|
|
147
|
+
|
|
148
|
+
### ๐ `config get`
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
saptools-sharepoint-excel config get --profile finance
|
|
152
|
+
saptools-sharepoint-excel config get --profile finance --json
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Secrets are never printed.
|
|
156
|
+
|
|
157
|
+
### ๐งน `config remove`
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
saptools-sharepoint-excel config remove --profile finance
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Removes both profile metadata and the stored secret.
|
|
164
|
+
|
|
165
|
+
### โ
`test`
|
|
166
|
+
|
|
167
|
+
Authenticate, resolve the site, and list document libraries.
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
saptools-sharepoint-excel test
|
|
171
|
+
saptools-sharepoint-excel test --json
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### ๐๏ธ `drives`
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
saptools-sharepoint-excel drives
|
|
178
|
+
saptools-sharepoint-excel drives --json
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Use this when you are unsure whether the document library is named `Documents`, `Shared Documents`, or something custom.
|
|
182
|
+
|
|
183
|
+
### ๐ `create`
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
saptools-sharepoint-excel create \
|
|
187
|
+
--path "Reports/orders.xlsx" \
|
|
188
|
+
--sheet "Orders" \
|
|
189
|
+
--headers "Name,Amount,Status" \
|
|
190
|
+
--rows '[{"Name":"Coffee","Amount":3,"Status":"open"}]' \
|
|
191
|
+
--table "OrdersTable"
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
`create` fails if `Reports/orders.xlsx` already exists.
|
|
195
|
+
|
|
196
|
+
### ๐ `read`
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
saptools-sharepoint-excel read --path "Reports/orders.xlsx"
|
|
200
|
+
saptools-sharepoint-excel read --path "Reports/orders.xlsx" --sheet "Orders" --range "A1:C10" --json
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### โ `append`
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
saptools-sharepoint-excel append \
|
|
207
|
+
--path "Reports/orders.xlsx" \
|
|
208
|
+
--sheet "Orders" \
|
|
209
|
+
--record '{"Name":"Tea","Amount":8,"Status":"open"}'
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Objects are mapped by the first row's headers by default. Use `--no-match-header` to append object values by their JSON key order instead.
|
|
213
|
+
|
|
214
|
+
### ๐ฏ `update-cell`
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
saptools-sharepoint-excel update-cell \
|
|
218
|
+
--path "Reports/orders.xlsx" \
|
|
219
|
+
--sheet "Orders" \
|
|
220
|
+
--cell "B2" \
|
|
221
|
+
--value "42"
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
`--value` accepts a JSON scalar (`42`, `true`, `null`, `"text"`) or a raw string.
|
|
225
|
+
|
|
226
|
+
### ๐ `add-sheet`
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
saptools-sharepoint-excel add-sheet \
|
|
230
|
+
--path "Reports/orders.xlsx" \
|
|
231
|
+
--sheet "Audit" \
|
|
232
|
+
--headers "At,Action,Actor"
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Fails if the sheet already exists.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## ๐ Credential Security
|
|
240
|
+
|
|
241
|
+
`@saptools/sharepoint-excel` handles Microsoft Graph app secrets. Treat them like production credentials.
|
|
242
|
+
|
|
243
|
+
- The CLI never prints `clientSecret`.
|
|
244
|
+
- Default secret storage uses OS-provided credential storage via `@napi-rs/keyring`.
|
|
245
|
+
- Local profile metadata lives under `~/.saptools/sharepoint-excel/profiles.json` with `0600` permissions.
|
|
246
|
+
- Plaintext secret files require explicit opt-in and should only be used in controlled automation.
|
|
247
|
+
- Mutating workbook commands use SharePoint ETags so a stale local download cannot silently replace a newer SharePoint edit.
|
|
248
|
+
|
|
249
|
+
Required Graph application permissions depend on your tenant model. Typical setups use `Sites.Selected` with site-specific grant plus file read/write access, or a broader `Files.ReadWrite.All`/`Sites.ReadWrite.All` policy where your organization permits it.
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## ๐งโ๐ป Programmatic Usage
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
import {
|
|
257
|
+
appendRemoteWorkbookRows,
|
|
258
|
+
createRemoteWorkbook,
|
|
259
|
+
openSession,
|
|
260
|
+
parseSiteRef,
|
|
261
|
+
} from "@saptools/sharepoint-excel";
|
|
262
|
+
|
|
263
|
+
const session = await openSession({
|
|
264
|
+
credentials: {
|
|
265
|
+
tenantId: process.env.SHAREPOINT_EXCEL_TENANT_ID ?? "",
|
|
266
|
+
clientId: process.env.SHAREPOINT_EXCEL_CLIENT_ID ?? "",
|
|
267
|
+
clientSecret: process.env.SHAREPOINT_EXCEL_CLIENT_SECRET ?? "",
|
|
268
|
+
},
|
|
269
|
+
site: parseSiteRef("contoso.sharepoint.com/sites/demo"),
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
await createRemoteWorkbook(
|
|
273
|
+
{ session, driveHint: "Documents" },
|
|
274
|
+
"Reports/orders.xlsx",
|
|
275
|
+
{
|
|
276
|
+
sheetName: "Orders",
|
|
277
|
+
headers: ["Name", "Amount"],
|
|
278
|
+
rows: [{ Name: "Coffee", Amount: 3 }],
|
|
279
|
+
},
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
await appendRemoteWorkbookRows(
|
|
283
|
+
{ session, driveHint: "Documents" },
|
|
284
|
+
"Reports/orders.xlsx",
|
|
285
|
+
"Orders",
|
|
286
|
+
[{ Name: "Tea", Amount: 8 }],
|
|
287
|
+
true,
|
|
288
|
+
);
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
<details>
|
|
292
|
+
<summary><b>๐ Main exports</b></summary>
|
|
293
|
+
|
|
294
|
+
| Export | Description |
|
|
295
|
+
| --- | --- |
|
|
296
|
+
| `resolveRuntime()` | Resolve flags/env/profile into a SharePoint target |
|
|
297
|
+
| `createProfileStore()` | Read/write redacted local profile metadata |
|
|
298
|
+
| `createKeyringSecretVault()` | OS credential vault adapter |
|
|
299
|
+
| `acquireAppToken()` | Request an app-only Graph token |
|
|
300
|
+
| `createGraphClient()` | Minimal Graph JSON/binary client |
|
|
301
|
+
| `parseSiteRef()` / `resolveSite()` | Parse and resolve SharePoint site references |
|
|
302
|
+
| `listDrives()` / `selectDrive()` | Discover and select document libraries |
|
|
303
|
+
| `createWorkbookBytes()` | Build a local `.xlsx` workbook |
|
|
304
|
+
| `readWorkbookBytes()` | Read sheet rows from workbook bytes |
|
|
305
|
+
| `appendWorkbookRows()` | Append rows in workbook bytes |
|
|
306
|
+
| `updateWorkbookCell()` | Update an A1 cell in workbook bytes |
|
|
307
|
+
| `addWorkbookSheet()` | Add a new worksheet |
|
|
308
|
+
| `createRemoteWorkbook()` | Create SharePoint workbook without overwriting |
|
|
309
|
+
| `readRemoteWorkbook()` | Download and read SharePoint workbook |
|
|
310
|
+
| `appendRemoteWorkbookRows()` | Append rows and upload with ETag protection |
|
|
311
|
+
| `updateRemoteWorkbookCell()` | Update one cell and upload with ETag protection |
|
|
312
|
+
| `addRemoteWorkbookSheet()` | Add a worksheet and upload with ETag protection |
|
|
313
|
+
|
|
314
|
+
</details>
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## ๐ ๏ธ Development
|
|
319
|
+
|
|
320
|
+
From the monorepo root:
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
pnpm install
|
|
324
|
+
pnpm --filter @saptools/sharepoint-excel lint
|
|
325
|
+
pnpm --filter @saptools/sharepoint-excel typecheck
|
|
326
|
+
pnpm --filter @saptools/sharepoint-excel build
|
|
327
|
+
pnpm --filter @saptools/sharepoint-excel test:unit
|
|
328
|
+
pnpm --filter @saptools/sharepoint-excel test:e2e:fake
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
The e2e suite uses a fake Microsoft Graph server and does not call real SharePoint.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## ๐ Related
|
|
336
|
+
|
|
337
|
+
- ๐ [`@saptools/sharepoint-check`](https://www.npmjs.com/package/@saptools/sharepoint-check) โ pre-flight Graph and SharePoint diagnostics
|
|
338
|
+
- ๐๏ธ [saptools monorepo](https://github.com/dongitran/saptools) โ the full toolbox
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## ๐จโ๐ป Author
|
|
343
|
+
|
|
344
|
+
**dongtran** โจ
|
|
345
|
+
|
|
346
|
+
## ๐ License
|
|
347
|
+
|
|
348
|
+
MIT
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
Made with โค๏ธ to make your work life easier!
|
package/dist/cli.d.ts
ADDED