@lindle/sharepoint_requests 0.1.18 → 0.1.20
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 +121 -116
- package/dist/index.d.mts +469 -0
- package/dist/index.d.ts +460 -8
- package/dist/index.js +892 -5
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +863 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +5 -5
- package/src/index.ts +3 -5
- package/src/root/index.ts +281 -146
- package/src/root/instance.ts +7 -0
- package/src/root/internal/context.ts +2 -3
- package/src/root/internal/digest.ts +18 -11
- package/src/root/internal/emailRequests.ts +5 -11
- package/src/root/internal/listRequests/createAttachment.ts +16 -5
- package/src/root/internal/listRequests/createListItem.ts +18 -27
- package/src/root/internal/listRequests/deleteFile.ts +5 -5
- package/src/root/internal/listRequests/deleteItem.ts +17 -3
- package/src/root/internal/listRequests/getFiles.ts +4 -10
- package/src/root/internal/listRequests/getListItems.ts +43 -9
- package/src/root/internal/listRequests/getOnly.ts +10 -7
- package/src/root/internal/listRequests/normalizePayload.ts +1 -1
- package/src/root/internal/listRequests/resolveMetadataType.ts +14 -0
- package/src/root/internal/listRequests/updateListItem.ts +24 -30
- package/src/root/internal/listRequests/uploadFile.ts +13 -21
- package/src/root/internal/listRequests/utils.ts +1 -1
- package/src/root/internal/odata.ts +2 -4
- package/src/root/internal/sharepointUrl.ts +7 -0
- package/src/root/internal/userRequests.ts +93 -37
- package/src/types.ts +154 -149
- package/dist/root/index.d.ts +0 -88
- package/dist/root/instance.d.ts +0 -3
- package/dist/root/internal/context.d.ts +0 -11
- package/dist/root/internal/digest.d.ts +0 -2
- package/dist/root/internal/emailRequests.d.ts +0 -10
- package/dist/root/internal/listRequests/createAttachment.d.ts +0 -3
- package/dist/root/internal/listRequests/createListItem.d.ts +0 -3
- package/dist/root/internal/listRequests/deleteFile.d.ts +0 -3
- package/dist/root/internal/listRequests/deleteItem.d.ts +0 -3
- package/dist/root/internal/listRequests/getFiles.d.ts +0 -8
- package/dist/root/internal/listRequests/getListItems.d.ts +0 -8
- package/dist/root/internal/listRequests/getOnly.d.ts +0 -3
- package/dist/root/internal/listRequests/index.d.ts +0 -9
- package/dist/root/internal/listRequests/normalizePayload.d.ts +0 -4
- package/dist/root/internal/listRequests/updateListItem.d.ts +0 -3
- package/dist/root/internal/listRequests/uploadFile.d.ts +0 -6
- package/dist/root/internal/listRequests/utils.d.ts +0 -2
- package/dist/root/internal/odata.d.ts +0 -5
- package/dist/root/internal/userRequests.d.ts +0 -15
- package/dist/sharepoint_requests.cjs.development.js +0 -1325
- package/dist/sharepoint_requests.cjs.development.js.map +0 -1
- package/dist/sharepoint_requests.cjs.production.min.js +0 -2
- package/dist/sharepoint_requests.cjs.production.min.js.map +0 -1
- package/dist/sharepoint_requests.esm.js +0 -1318
- package/dist/sharepoint_requests.esm.js.map +0 -1
- package/dist/types.d.ts +0 -256
package/src/root/index.ts
CHANGED
|
@@ -2,41 +2,64 @@ import { AxiosInstance, CreateAxiosDefaults as AxiosConfig } from 'axios';
|
|
|
2
2
|
import instance from './instance';
|
|
3
3
|
import {
|
|
4
4
|
CreateFileProps,
|
|
5
|
-
DeleteFileProps,
|
|
6
5
|
CurrentUser,
|
|
6
|
+
DeleteFileProps,
|
|
7
7
|
EmailProps,
|
|
8
8
|
Field_Options,
|
|
9
9
|
Folder_Options,
|
|
10
|
-
UploadFileProps,
|
|
11
|
-
IListName,
|
|
12
10
|
ItemID,
|
|
13
11
|
ListData,
|
|
14
12
|
Options,
|
|
15
13
|
PersonField,
|
|
16
14
|
SHAREPOINT_VER,
|
|
15
|
+
UploadFileProps,
|
|
17
16
|
UserPermision,
|
|
18
17
|
UserProfile,
|
|
19
18
|
} from '../types';
|
|
20
19
|
import { RequestContext } from './internal/context';
|
|
20
|
+
import { sendEmail as sendEmailRequest } from './internal/emailRequests';
|
|
21
|
+
import { fetchDigest } from './internal/digest';
|
|
21
22
|
import {
|
|
22
23
|
createAttachment,
|
|
23
24
|
createListItem,
|
|
24
|
-
deleteItem,
|
|
25
25
|
deleteFile as deleteLibraryFile,
|
|
26
|
+
deleteItem,
|
|
26
27
|
getFiles,
|
|
27
28
|
getListItems,
|
|
28
29
|
getOnly,
|
|
29
30
|
updateListItem,
|
|
30
31
|
uploadFile,
|
|
31
32
|
} from './internal/listRequests';
|
|
32
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
buildListByTitleEndpoint,
|
|
35
|
+
escapeODataStringLiteral,
|
|
36
|
+
} from './internal/sharepointUrl';
|
|
33
37
|
import {
|
|
34
38
|
currentUserPermissions as resolveCurrentUserPermissions,
|
|
35
39
|
currentUserProperties as loadCurrentUserProperties,
|
|
36
40
|
getSiteUser as fetchSiteUser,
|
|
37
41
|
typeAhead,
|
|
38
42
|
} from './internal/userRequests';
|
|
39
|
-
|
|
43
|
+
|
|
44
|
+
type ListKey<T extends { LISTS: Record<string, unknown> }> = Extract<
|
|
45
|
+
keyof T['LISTS'],
|
|
46
|
+
string
|
|
47
|
+
>;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @deprecated SP.Utilities.Utility.SendEmail was retired by Microsoft on 2025-10-31
|
|
51
|
+
* and no longer sends emails. Migrate to Microsoft Graph API:
|
|
52
|
+
* POST /v1.0/me/sendMail (delegated) or POST /v1.0/users/{id}/sendMail (app)
|
|
53
|
+
* https://learn.microsoft.com/en-us/graph/api/user-sendmail
|
|
54
|
+
*/
|
|
55
|
+
type EmailApi = ((
|
|
56
|
+
from: string,
|
|
57
|
+
to: string[] | string,
|
|
58
|
+
subject: string | undefined,
|
|
59
|
+
body: string
|
|
60
|
+
) => ReturnType<typeof sendEmailRequest>) & {
|
|
61
|
+
send: (props: EmailProps) => ReturnType<typeof sendEmailRequest>;
|
|
62
|
+
};
|
|
40
63
|
|
|
41
64
|
class HTTPSharePointRequests<
|
|
42
65
|
T extends {
|
|
@@ -44,52 +67,79 @@ class HTTPSharePointRequests<
|
|
|
44
67
|
FOLDERS?: Record<string, unknown>;
|
|
45
68
|
}
|
|
46
69
|
> {
|
|
47
|
-
private baseURL: string;
|
|
48
|
-
private
|
|
49
|
-
private
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
70
|
+
private readonly baseURL: string;
|
|
71
|
+
private readonly instance: AxiosInstance;
|
|
72
|
+
private readonly listItemEntityTypeCache: Map<string, string>;
|
|
73
|
+
/**
|
|
74
|
+
* @deprecated SP.Utilities.Utility.SendEmail was retired by Microsoft on 2025-10-31.
|
|
75
|
+
* Migrate to Microsoft Graph API POST /v1.0/me/sendMail
|
|
76
|
+
* https://learn.microsoft.com/en-us/graph/api/user-sendmail
|
|
77
|
+
*/
|
|
78
|
+
public readonly email: EmailApi;
|
|
53
79
|
|
|
54
80
|
constructor(baseURL: string, config?: AxiosConfig) {
|
|
55
81
|
this.baseURL = baseURL.endsWith('/') ? baseURL : `${baseURL}/`;
|
|
56
|
-
this.endpoint = '/api';
|
|
57
|
-
this.listName = '';
|
|
58
82
|
this.instance = instance(this.baseURL, config);
|
|
59
|
-
this.
|
|
60
|
-
|
|
83
|
+
this.listItemEntityTypeCache = new Map<string, string>();
|
|
84
|
+
const buildEmailContext = () =>
|
|
85
|
+
this.createContext<'2013'>({
|
|
86
|
+
endpoint: '_api/SP.Utilities.Utility.SendEmail',
|
|
87
|
+
listName: '',
|
|
88
|
+
sharepointVersion: '2013',
|
|
89
|
+
});
|
|
90
|
+
this.email = Object.assign(
|
|
91
|
+
(
|
|
92
|
+
from: string,
|
|
93
|
+
to: string[] | string,
|
|
94
|
+
subject: string | undefined,
|
|
95
|
+
body: string
|
|
96
|
+
) =>
|
|
97
|
+
sendEmailRequest(buildEmailContext(), {
|
|
98
|
+
From: from,
|
|
99
|
+
To: to,
|
|
100
|
+
Subject: subject,
|
|
101
|
+
Body: body,
|
|
102
|
+
}),
|
|
103
|
+
{
|
|
104
|
+
send: (props: EmailProps) => sendEmailRequest(buildEmailContext(), props),
|
|
105
|
+
}
|
|
106
|
+
);
|
|
61
107
|
}
|
|
62
108
|
|
|
63
|
-
private
|
|
109
|
+
private createContext<V extends SHAREPOINT_VER = '2013'>({
|
|
110
|
+
endpoint,
|
|
111
|
+
listName,
|
|
112
|
+
sharepointVersion,
|
|
113
|
+
listItemEntityTypeFullName,
|
|
114
|
+
resolveListItemEntityType,
|
|
115
|
+
}: {
|
|
116
|
+
endpoint: string;
|
|
117
|
+
listName: string;
|
|
118
|
+
sharepointVersion: V;
|
|
119
|
+
listItemEntityTypeFullName?: string;
|
|
120
|
+
resolveListItemEntityType?: () => Promise<string>;
|
|
121
|
+
}): RequestContext<V> {
|
|
64
122
|
return {
|
|
65
123
|
instance: this.instance,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
resolveListItemEntityType: this.resolveListItemEntityTypeFullName.bind(
|
|
72
|
-
this
|
|
73
|
-
),
|
|
124
|
+
endpoint,
|
|
125
|
+
listName,
|
|
126
|
+
sharepointVersion,
|
|
127
|
+
listItemEntityTypeFullName,
|
|
128
|
+
resolveListItemEntityType,
|
|
74
129
|
};
|
|
75
130
|
}
|
|
76
131
|
|
|
77
|
-
private async resolveListItemEntityTypeFullName(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (this.sharepointVersion !== '2013') {
|
|
87
|
-
this.listItemEntityTypeFullName = `SP.Data.${this.listName}ListItem`;
|
|
88
|
-
return this.listItemEntityTypeFullName;
|
|
132
|
+
private async resolveListItemEntityTypeFullName(
|
|
133
|
+
listName: string
|
|
134
|
+
): Promise<string> {
|
|
135
|
+
const cacheKey = listName;
|
|
136
|
+
const cachedType = this.listItemEntityTypeCache.get(cacheKey);
|
|
137
|
+
if (cachedType) {
|
|
138
|
+
return cachedType;
|
|
89
139
|
}
|
|
90
140
|
|
|
91
141
|
const response = await this.instance.get(
|
|
92
|
-
|
|
142
|
+
`${buildListByTitleEndpoint(listName)}?$select=ListItemEntityTypeFullName`
|
|
93
143
|
);
|
|
94
144
|
|
|
95
145
|
const entityType =
|
|
@@ -98,161 +148,246 @@ class HTTPSharePointRequests<
|
|
|
98
148
|
|
|
99
149
|
if (!entityType || typeof entityType !== 'string') {
|
|
100
150
|
throw new Error(
|
|
101
|
-
`Unable to resolve ListItemEntityTypeFullName for list "${
|
|
151
|
+
`Unable to resolve ListItemEntityTypeFullName for list "${listName}".`
|
|
102
152
|
);
|
|
103
153
|
}
|
|
104
154
|
|
|
105
|
-
this.
|
|
106
|
-
return
|
|
155
|
+
this.listItemEntityTypeCache.set(cacheKey, entityType);
|
|
156
|
+
return entityType;
|
|
107
157
|
}
|
|
108
158
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
159
|
+
private buildListScope<
|
|
160
|
+
K extends ListKey<T>,
|
|
161
|
+
V extends SHAREPOINT_VER = '2013'
|
|
162
|
+
>(listName: K, version: V) {
|
|
163
|
+
const listNameValue = String(listName);
|
|
164
|
+
const endpoint =
|
|
165
|
+
version === '2013'
|
|
166
|
+
? `${buildListByTitleEndpoint(listNameValue)}/items`
|
|
167
|
+
: `_vti_bin/ListData.svc/${encodeURIComponent(listNameValue)}`;
|
|
119
168
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
},
|
|
126
|
-
};
|
|
169
|
+
let scopedEntityType: string | undefined;
|
|
170
|
+
const resolveListItemEntityType = async () => {
|
|
171
|
+
if (scopedEntityType) {
|
|
172
|
+
return scopedEntityType;
|
|
173
|
+
}
|
|
127
174
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
175
|
+
if (version !== '2013') {
|
|
176
|
+
scopedEntityType = `SP.Data.${listNameValue}ListItem`;
|
|
177
|
+
return scopedEntityType;
|
|
178
|
+
}
|
|
131
179
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
180
|
+
scopedEntityType = await this.resolveListItemEntityTypeFullName(
|
|
181
|
+
listNameValue
|
|
182
|
+
);
|
|
183
|
+
return scopedEntityType;
|
|
184
|
+
};
|
|
137
185
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
186
|
+
const getContext = () =>
|
|
187
|
+
this.createContext<V>({
|
|
188
|
+
endpoint,
|
|
189
|
+
listName: listNameValue,
|
|
190
|
+
sharepointVersion: version,
|
|
191
|
+
listItemEntityTypeFullName: scopedEntityType,
|
|
192
|
+
resolveListItemEntityType,
|
|
193
|
+
});
|
|
145
194
|
|
|
146
|
-
const
|
|
195
|
+
const removeItem = (id: ItemID) => deleteItem(getContext(), id);
|
|
196
|
+
const removeFile = (props: DeleteFileProps) =>
|
|
197
|
+
deleteLibraryFile(getContext(), props);
|
|
147
198
|
|
|
148
199
|
return {
|
|
149
200
|
create: (data: ListData<T['LISTS'][K]>) =>
|
|
150
201
|
createListItem<T['LISTS'][K], V>(getContext(), data),
|
|
151
202
|
get: (options?: Options<T['LISTS'][K], V>) =>
|
|
152
203
|
getListItems<T['LISTS'][K], V>(getContext(), options),
|
|
153
|
-
update: (
|
|
154
|
-
id
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
) => updateListItem<T['LISTS'][K], V>(getContext(), id, data, digest),
|
|
158
|
-
delete: (id: ItemID) => deleteItem(getContext(), id),
|
|
204
|
+
update: (id: ItemID, data: ListData<T['LISTS'][K]>, digest?: string) =>
|
|
205
|
+
updateListItem<T['LISTS'][K], V>(getContext(), id, data, digest),
|
|
206
|
+
delete: removeItem,
|
|
207
|
+
remove: removeItem,
|
|
159
208
|
createFile: (props: CreateFileProps) =>
|
|
160
209
|
createAttachment(getContext(), props),
|
|
161
210
|
uploadFile: (props: UploadFileProps) => uploadFile(getContext(), props),
|
|
162
|
-
deleteFile:
|
|
163
|
-
|
|
211
|
+
deleteFile: removeFile,
|
|
212
|
+
removeFile,
|
|
164
213
|
fields: (options?: Field_Options<T['LISTS'][K], V>) => {
|
|
165
|
-
|
|
166
|
-
?
|
|
167
|
-
:
|
|
214
|
+
const fieldsEndpoint = !options?.fieldName
|
|
215
|
+
? `${buildListByTitleEndpoint(listNameValue)}/fields`
|
|
216
|
+
: `${buildListByTitleEndpoint(
|
|
217
|
+
listNameValue
|
|
218
|
+
)}/fields/getbytitle('${escapeODataStringLiteral(
|
|
219
|
+
String(options.fieldName)
|
|
220
|
+
)}')`;
|
|
168
221
|
return {
|
|
169
222
|
get: () =>
|
|
170
|
-
getOnly<T['LISTS'][K], V>(this.instance,
|
|
223
|
+
getOnly<T['LISTS'][K], V>(this.instance, fieldsEndpoint, options),
|
|
171
224
|
};
|
|
172
225
|
},
|
|
226
|
+
items: {
|
|
227
|
+
create: (data: ListData<T['LISTS'][K]>) =>
|
|
228
|
+
createListItem<T['LISTS'][K], V>(getContext(), data),
|
|
229
|
+
get: (options?: Options<T['LISTS'][K], V>) =>
|
|
230
|
+
getListItems<T['LISTS'][K], V>(getContext(), options),
|
|
231
|
+
update: (id: ItemID, data: ListData<T['LISTS'][K]>, digest?: string) =>
|
|
232
|
+
updateListItem<T['LISTS'][K], V>(getContext(), id, data, digest),
|
|
233
|
+
delete: removeItem,
|
|
234
|
+
remove: removeItem,
|
|
235
|
+
},
|
|
236
|
+
files: {
|
|
237
|
+
attach: (props: CreateFileProps) =>
|
|
238
|
+
createAttachment(getContext(), props),
|
|
239
|
+
upload: (props: UploadFileProps) => uploadFile(getContext(), props),
|
|
240
|
+
delete: removeFile,
|
|
241
|
+
remove: removeFile,
|
|
242
|
+
},
|
|
173
243
|
};
|
|
174
244
|
}
|
|
175
245
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
};
|
|
246
|
+
/**
|
|
247
|
+
* @deprecated Use `from(listName, '2010')` instead.
|
|
248
|
+
*/
|
|
249
|
+
list<K extends ListKey<T>>(listName: K) {
|
|
250
|
+
return this.from<K, '2010'>(listName, '2010');
|
|
251
|
+
}
|
|
183
252
|
|
|
184
|
-
public
|
|
185
|
-
|
|
253
|
+
public from<K extends ListKey<T>, V extends SHAREPOINT_VER = '2013'>(
|
|
254
|
+
listName: K,
|
|
255
|
+
sharepointVersion?: V
|
|
256
|
+
) {
|
|
257
|
+
const version = (sharepointVersion ?? '2013') as V;
|
|
258
|
+
return this.buildListScope<K, V>(listName, version);
|
|
259
|
+
}
|
|
186
260
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
261
|
+
public field<K extends ListKey<T>, V extends SHAREPOINT_VER = '2013'>(
|
|
262
|
+
listName: K,
|
|
263
|
+
fieldName?: Extract<keyof T['LISTS'][K], string>,
|
|
264
|
+
sharepointVersion?: V
|
|
265
|
+
) {
|
|
266
|
+
return this.from<K, V>(listName, sharepointVersion)
|
|
267
|
+
.fields({
|
|
268
|
+
fieldName,
|
|
269
|
+
} as Field_Options<T['LISTS'][K], V>)
|
|
270
|
+
.get();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
public lists(options?: Options<Record<string, unknown>, '2013'>) {
|
|
274
|
+
const context = this.createContext<'2013'>({
|
|
275
|
+
endpoint: '_api/web/lists',
|
|
276
|
+
listName: '',
|
|
277
|
+
sharepointVersion: '2013',
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
get: () => getListItems<Record<string, unknown>, '2013'>(context, options),
|
|
282
|
+
};
|
|
283
|
+
}
|
|
192
284
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
this.sharepointVersion = '2013';
|
|
285
|
+
public getDigest() {
|
|
286
|
+
return fetchDigest(this.instance);
|
|
287
|
+
}
|
|
197
288
|
|
|
198
|
-
|
|
289
|
+
public folders(options?: Folder_Options<Record<string, unknown>>) {
|
|
290
|
+
const context = this.createContext<'2013'>({
|
|
291
|
+
endpoint: '_api/web/folders',
|
|
292
|
+
listName: '',
|
|
293
|
+
sharepointVersion: '2013',
|
|
294
|
+
});
|
|
199
295
|
|
|
200
296
|
return {
|
|
201
|
-
get: (
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
297
|
+
get: () => getFiles<Record<string, unknown>, '2013'>(context, options),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
public folder(folderName?: string | string[]) {
|
|
302
|
+
const defaultPath = ['Shared Documents'];
|
|
303
|
+
const segments = folderName
|
|
304
|
+
? Array.isArray(folderName)
|
|
305
|
+
? [...defaultPath, ...folderName]
|
|
306
|
+
: [...defaultPath, ...folderName.split('/').filter(Boolean)]
|
|
307
|
+
: defaultPath;
|
|
308
|
+
|
|
309
|
+
const serverRelativePath = escapeODataStringLiteral(segments.join('/'));
|
|
310
|
+
const baseEndpoint = `_api/web/GetFolderByServerRelativeUrl('${serverRelativePath}')`;
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
get: (options?: Folder_Options<Record<string, unknown>>) => {
|
|
314
|
+
const folderType = options?.type ?? 'Files';
|
|
315
|
+
const endpoint = `${baseEndpoint}/${folderType}`;
|
|
316
|
+
const context = this.createContext<'2013'>({
|
|
317
|
+
endpoint,
|
|
318
|
+
listName: '',
|
|
319
|
+
sharepointVersion: '2013',
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
return getFiles<Record<string, unknown>, '2013'>(context, {
|
|
323
|
+
...options,
|
|
324
|
+
type: folderType,
|
|
325
|
+
});
|
|
208
326
|
},
|
|
209
327
|
};
|
|
210
328
|
}
|
|
211
329
|
|
|
212
|
-
public users(options?: Options<
|
|
213
|
-
|
|
214
|
-
|
|
330
|
+
public users(options?: Options<Record<string, unknown>, '2013'>) {
|
|
331
|
+
const context = this.createContext<'2013'>({
|
|
332
|
+
endpoint: '_api/web/SiteUserInfoList/items',
|
|
333
|
+
listName: '',
|
|
334
|
+
sharepointVersion: '2013',
|
|
335
|
+
});
|
|
336
|
+
|
|
215
337
|
return {
|
|
216
|
-
get: () => getListItems(
|
|
338
|
+
get: () => getListItems<Record<string, unknown>, '2013'>(context, options),
|
|
217
339
|
};
|
|
218
340
|
}
|
|
219
341
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
342
|
+
/**
|
|
343
|
+
* @deprecated SP.Utilities.Utility.SendEmail was retired by Microsoft on 2025-10-31.
|
|
344
|
+
* Migrate to Microsoft Graph API POST /v1.0/me/sendMail
|
|
345
|
+
* https://learn.microsoft.com/en-us/graph/api/user-sendmail
|
|
346
|
+
*/
|
|
347
|
+
public sendEmail(props: EmailProps) {
|
|
348
|
+
const context = this.createContext<'2013'>({
|
|
349
|
+
endpoint: '_api/SP.Utilities.Utility.SendEmail',
|
|
350
|
+
listName: '',
|
|
351
|
+
sharepointVersion: '2013',
|
|
352
|
+
});
|
|
353
|
+
return sendEmailRequest(context, props);
|
|
354
|
+
}
|
|
226
355
|
|
|
227
|
-
public user = {
|
|
356
|
+
public readonly user = {
|
|
228
357
|
properties: () => ({
|
|
229
|
-
get: (): Promise<UserProfile> =>
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
358
|
+
get: (): Promise<UserProfile> => loadCurrentUserProperties(this.instance),
|
|
359
|
+
}),
|
|
360
|
+
searchSiteUser: (searchValue: string) =>
|
|
361
|
+
fetchSiteUser(this.instance, searchValue),
|
|
362
|
+
searchUser: (input: string, filter?: string): Promise<PersonField> =>
|
|
363
|
+
typeAhead(this.instance, input, filter),
|
|
364
|
+
current: () => ({
|
|
365
|
+
get: (): Promise<CurrentUser> =>
|
|
366
|
+
getOnly(this.instance, '_api/web/currentUser'),
|
|
233
367
|
}),
|
|
234
|
-
searchSiteUser: (searchValue: string) => {
|
|
235
|
-
this.sharepointVersion = '2013';
|
|
236
|
-
return fetchSiteUser(this.instance, searchValue);
|
|
237
|
-
},
|
|
238
|
-
searchUser: (input: string, filter?: string): Promise<PersonField> => {
|
|
239
|
-
this.sharepointVersion = '2013';
|
|
240
|
-
return typeAhead(this.instance, input, filter);
|
|
241
|
-
},
|
|
242
|
-
current: () => {
|
|
243
|
-
this.endpoint = '_api/web/currentUser';
|
|
244
|
-
this.sharepointVersion = '2013';
|
|
245
|
-
return {
|
|
246
|
-
get: (): Promise<CurrentUser> => getOnly(this.instance, this.endpoint),
|
|
247
|
-
};
|
|
248
|
-
},
|
|
249
368
|
currentPermission: () => ({
|
|
250
|
-
get: (): Promise<UserPermision> =>
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
369
|
+
get: (): Promise<UserPermision> =>
|
|
370
|
+
resolveCurrentUserPermissions(
|
|
371
|
+
this.createContext<'2013'>({
|
|
372
|
+
endpoint: '_api/web/effectiveBasePermissions',
|
|
373
|
+
listName: '',
|
|
374
|
+
sharepointVersion: '2013',
|
|
375
|
+
})
|
|
376
|
+
),
|
|
254
377
|
}),
|
|
255
378
|
};
|
|
379
|
+
|
|
380
|
+
public getCurrentUser() {
|
|
381
|
+
return this.user.current().get();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
public currentUserProperties() {
|
|
385
|
+
return this.user.properties().get();
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
public currentUserPermission() {
|
|
389
|
+
return this.user.currentPermission().get();
|
|
390
|
+
}
|
|
256
391
|
}
|
|
257
392
|
|
|
258
393
|
export default HTTPSharePointRequests;
|
package/src/root/instance.ts
CHANGED
|
@@ -7,6 +7,13 @@ const defaultConfig: AxiosConfig = {
|
|
|
7
7
|
},
|
|
8
8
|
timeout: 15000,
|
|
9
9
|
withCredentials: true,
|
|
10
|
+
paramsSerializer: {
|
|
11
|
+
serialize: (params: Record<string, string | number | undefined>) =>
|
|
12
|
+
Object.entries(params)
|
|
13
|
+
.filter(([, v]) => v !== undefined && v !== null)
|
|
14
|
+
.map(([k, v]) => `${k}=${String(v)}`)
|
|
15
|
+
.join('&'),
|
|
16
|
+
},
|
|
10
17
|
};
|
|
11
18
|
|
|
12
19
|
export const instance = (baseURL: string, config?: AxiosConfig) => {
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
2
|
+
import { SHAREPOINT_VER } from '../../types';
|
|
3
3
|
|
|
4
4
|
export interface RequestContext<V extends SHAREPOINT_VER = '2013'> {
|
|
5
5
|
instance: AxiosInstance;
|
|
6
|
-
baseURL: string;
|
|
7
6
|
endpoint: string;
|
|
8
7
|
listName: string;
|
|
9
8
|
sharepointVersion: V;
|
|
@@ -1,22 +1,29 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
baseURL: string
|
|
6
|
-
): Promise<string> {
|
|
7
|
-
try {
|
|
8
|
-
const response = await instance.post(`${baseURL}_api/contextinfo`);
|
|
3
|
+
type DigestEntry = { value: string; expiresAt: number };
|
|
4
|
+
const cache = new WeakMap<AxiosInstance, DigestEntry>();
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
export async function fetchDigest(instance: AxiosInstance): Promise<string> {
|
|
7
|
+
const cached = cache.get(instance);
|
|
8
|
+
if (cached && Date.now() < cached.expiresAt) {
|
|
9
|
+
return cached.value;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const response = await instance.post('_api/contextinfo');
|
|
14
|
+
const info = response.data?.d?.GetContextWebInformation;
|
|
15
|
+
const formDigestValue = info?.FormDigestValue;
|
|
12
16
|
|
|
13
17
|
if (!formDigestValue) {
|
|
14
18
|
throw new Error('Invalid response structure: Missing FormDigestValue');
|
|
15
19
|
}
|
|
16
20
|
|
|
21
|
+
const ttl = (info?.FormDigestTimeoutSeconds ?? 1800) - 60;
|
|
22
|
+
cache.set(instance, { value: formDigestValue, expiresAt: Date.now() + ttl * 1000 });
|
|
23
|
+
|
|
17
24
|
return formDigestValue;
|
|
18
25
|
} catch (error) {
|
|
19
|
-
|
|
20
|
-
throw new Error(`Error retrieving Form Digest Value`);
|
|
26
|
+
const cause = error instanceof Error ? error.message : 'Unknown contextinfo error';
|
|
27
|
+
throw new Error(`Error retrieving Form Digest Value: ${cause}`);
|
|
21
28
|
}
|
|
22
29
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { AxiosResponse } from 'axios';
|
|
2
|
+
import { EmailProps } from '../../types';
|
|
3
3
|
import { fetchDigest } from './digest';
|
|
4
|
-
import
|
|
4
|
+
import { RequestContext } from './context';
|
|
5
5
|
|
|
6
6
|
type SendEmailResponse = AxiosResponse<{
|
|
7
7
|
d: {
|
|
@@ -13,17 +13,11 @@ export async function sendEmail(
|
|
|
13
13
|
context: RequestContext,
|
|
14
14
|
{ From, To, Subject, Body }: EmailProps
|
|
15
15
|
): Promise<SendEmailResponse> {
|
|
16
|
-
const httpRequest = '_api/SP.Utilities.Utility.SendEmail';
|
|
17
|
-
|
|
18
16
|
const response = await context.instance({
|
|
19
|
-
url:
|
|
17
|
+
url: '_api/SP.Utilities.Utility.SendEmail',
|
|
20
18
|
method: 'POST',
|
|
21
19
|
headers: {
|
|
22
|
-
'X-RequestDigest': await fetchDigest(
|
|
23
|
-
context.instance,
|
|
24
|
-
context.baseURL
|
|
25
|
-
),
|
|
26
|
-
'X-Http-Method': 'POST',
|
|
20
|
+
'X-RequestDigest': await fetchDigest(context.instance),
|
|
27
21
|
'Content-Type': 'application/json;odata=verbose',
|
|
28
22
|
},
|
|
29
23
|
data: JSON.stringify({
|
|
@@ -1,17 +1,28 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { CreateFileProps, SHAREPOINT_VER } from '../../../types';
|
|
2
2
|
import { fetchDigest } from '../digest';
|
|
3
|
-
import
|
|
3
|
+
import { RequestContext } from '../context';
|
|
4
|
+
import { buildListByTitleEndpoint } from '../sharepointUrl';
|
|
5
|
+
import { encodeFileName } from './utils';
|
|
4
6
|
|
|
5
7
|
export async function createAttachment<V extends SHAREPOINT_VER = '2013'>(
|
|
6
8
|
context: RequestContext<V>,
|
|
7
9
|
{ file, itemId }: CreateFileProps
|
|
8
10
|
) {
|
|
9
|
-
|
|
11
|
+
if (context.sharepointVersion !== '2013') {
|
|
12
|
+
throw new Error(
|
|
13
|
+
'createAttachment is only supported for SharePoint 2013 REST endpoints.'
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const encodedFileName = encodeFileName(file.name);
|
|
18
|
+
const requestUrl = `${buildListByTitleEndpoint(
|
|
19
|
+
context.listName
|
|
20
|
+
)}/items(${itemId})/AttachmentFiles/add(FileName='${encodedFileName}')`;
|
|
21
|
+
|
|
10
22
|
const response = await context.instance.post(requestUrl, file, {
|
|
11
23
|
headers: {
|
|
12
|
-
'X-RequestDigest': await fetchDigest(context.instance
|
|
24
|
+
'X-RequestDigest': await fetchDigest(context.instance),
|
|
13
25
|
},
|
|
14
|
-
withCredentials: true,
|
|
15
26
|
});
|
|
16
27
|
|
|
17
28
|
return response;
|