easyproctor 0.0.9 → 0.0.13
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 +32 -39
- package/dtos/StartProctoringResponse.d.ts +9 -0
- package/esm/index.js +290 -13
- package/index.d.ts +15 -4
- package/index.js +301 -22
- package/modules/database.d.ts +6 -0
- package/modules/http.d.ts +6 -0
- package/modules/recorder.d.ts +1 -0
- package/modules/startCameraCapture.d.ts +1 -1
- package/modules/startScreenCapture.d.ts +1 -1
- package/modules/upload.d.ts +6 -0
- package/package.json +14 -7
- package/unpkg/easyproctor.min.js +22 -1
- package/test.d.ts +0 -1
package/index.js
CHANGED
|
@@ -1,56 +1,335 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
1
2
|
var __defProp = Object.defineProperty;
|
|
2
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
7
|
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
|
|
6
8
|
var __export = (target, all) => {
|
|
9
|
+
__markAsModule(target);
|
|
7
10
|
for (var name in all)
|
|
8
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
12
|
};
|
|
10
|
-
var __reExport = (target, module2,
|
|
13
|
+
var __reExport = (target, module2, desc) => {
|
|
11
14
|
if (module2 && typeof module2 === "object" || typeof module2 === "function") {
|
|
12
15
|
for (let key of __getOwnPropNames(module2))
|
|
13
|
-
if (!__hasOwnProp.call(target, key) &&
|
|
16
|
+
if (!__hasOwnProp.call(target, key) && key !== "default")
|
|
14
17
|
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable });
|
|
15
18
|
}
|
|
16
19
|
return target;
|
|
17
20
|
};
|
|
18
|
-
var
|
|
19
|
-
return (module2,
|
|
20
|
-
|
|
21
|
-
};
|
|
22
|
-
})(typeof WeakMap !== "undefined" ? /* @__PURE__ */ new WeakMap() : 0);
|
|
21
|
+
var __toModule = (module2) => {
|
|
22
|
+
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2);
|
|
23
|
+
};
|
|
23
24
|
|
|
24
25
|
// src/index.ts
|
|
25
|
-
|
|
26
|
-
__export(src_exports, {
|
|
26
|
+
__export(exports, {
|
|
27
27
|
useProctoring: () => useProctoring
|
|
28
28
|
});
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
|
|
30
|
+
// src/modules/database.ts
|
|
31
|
+
var databaseName = "EasyProctorPlugin";
|
|
32
|
+
var databaseVersion = 1;
|
|
33
|
+
function initializeDatabase(table) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const request = indexedDB.open(databaseName, databaseVersion);
|
|
36
|
+
request.onupgradeneeded = () => {
|
|
37
|
+
request.result.createObjectStore("cameraBuffers", { keyPath: "id", autoIncrement: true });
|
|
38
|
+
request.result.createObjectStore("screenBuffers", { keyPath: "id", autoIncrement: true });
|
|
39
|
+
};
|
|
40
|
+
request.onerror = (e) => {
|
|
41
|
+
console.log(e);
|
|
42
|
+
reject("N\xE3o foi poss\xEDvel inicializar a biblioteca, por favor, entre em contato com o suporte e informe o erro acima");
|
|
43
|
+
};
|
|
44
|
+
request.onsuccess = () => {
|
|
45
|
+
const db = request.result;
|
|
46
|
+
const tableRef = db.transaction(table, "readwrite");
|
|
47
|
+
const store = tableRef.objectStore(table);
|
|
48
|
+
resolve(store);
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async function getBuffers(table) {
|
|
53
|
+
const store = await initializeDatabase(table);
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
const request = store.getAll();
|
|
56
|
+
request.onsuccess = () => {
|
|
57
|
+
const data = request.result;
|
|
58
|
+
const blobs = data.reduce((acc, el) => [...acc, ...el.data], []);
|
|
59
|
+
resolve(blobs);
|
|
60
|
+
};
|
|
61
|
+
request.onerror = (e) => reject(e);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async function clearBuffers(table) {
|
|
65
|
+
const store = await initializeDatabase(table);
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
const request = store.clear();
|
|
68
|
+
request.onsuccess = () => resolve();
|
|
69
|
+
request.onerror = (e) => reject(e);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/modules/recorder.ts
|
|
74
|
+
function recorder(stream, buffer) {
|
|
75
|
+
let resolvePromise;
|
|
76
|
+
const options = {
|
|
77
|
+
mimeType: "video/webm; codecs=vp9",
|
|
78
|
+
videoBitsPerSecond: 128e3,
|
|
79
|
+
audioBitsPerSecond: 64 * 1e3
|
|
80
|
+
};
|
|
81
|
+
const mediaRecorder = new MediaRecorder(stream, options);
|
|
82
|
+
mediaRecorder.ondataavailable = (e) => {
|
|
83
|
+
if (e.data.size > 0) {
|
|
84
|
+
buffer.push(e.data);
|
|
85
|
+
}
|
|
86
|
+
resolvePromise && resolvePromise();
|
|
32
87
|
};
|
|
33
|
-
|
|
88
|
+
mediaRecorder.start();
|
|
89
|
+
function stopRecording() {
|
|
34
90
|
return new Promise((resolve) => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
91
|
+
resolvePromise = resolve;
|
|
92
|
+
mediaRecorder.stop();
|
|
93
|
+
stream.getTracks().forEach((el) => {
|
|
94
|
+
el.stop();
|
|
95
|
+
});
|
|
38
96
|
});
|
|
97
|
+
}
|
|
98
|
+
return stopRecording;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/modules/startCameraCapture.ts
|
|
102
|
+
async function startCameraCapture(buffer) {
|
|
103
|
+
const constraints = {
|
|
104
|
+
audio: true,
|
|
105
|
+
video: {
|
|
106
|
+
width: { max: 1280, ideal: 640 },
|
|
107
|
+
height: { max: 720, ideal: 480 },
|
|
108
|
+
frameRate: 15
|
|
109
|
+
}
|
|
39
110
|
};
|
|
40
|
-
const
|
|
41
|
-
|
|
111
|
+
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
112
|
+
const stopRecording = recorder(stream, buffer);
|
|
113
|
+
return stopRecording;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/modules/startScreenCapture.ts
|
|
117
|
+
async function startScreenCapture(buffer) {
|
|
118
|
+
const displayMediaStreamConstraints = {
|
|
119
|
+
video: {
|
|
120
|
+
cursor: "always"
|
|
121
|
+
},
|
|
122
|
+
audio: false
|
|
42
123
|
};
|
|
43
|
-
const
|
|
44
|
-
|
|
124
|
+
const stream = await navigator.mediaDevices.getDisplayMedia(displayMediaStreamConstraints);
|
|
125
|
+
const stopRecording = recorder(stream, buffer);
|
|
126
|
+
return stopRecording;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// src/modules/upload.ts
|
|
130
|
+
var import_storage_blob = __toModule(require("@azure/storage-blob"));
|
|
131
|
+
var account = "iarisprod";
|
|
132
|
+
var containerName = "iaris";
|
|
133
|
+
var sas = "?sv=2020-08-04&ss=bfqt&srt=sco&sp=rwdlacupitfx&se=2025-12-28T06:34:02Z&st=2021-12-27T22:34:02Z&spr=https&sig=1rsgx389pHZCnJYd44peuWSfeCUdN8bQ9EfcLoMOdDc%3D";
|
|
134
|
+
var blobServiceClient = new import_storage_blob.BlobServiceClient(`https://${account}.blob.core.windows.net${sas}`);
|
|
135
|
+
async function upload(data) {
|
|
136
|
+
const { file, onProgress } = data;
|
|
137
|
+
const containerClient = blobServiceClient.getContainerClient(containerName);
|
|
138
|
+
const blockBlobClient = containerClient.getBlockBlobClient(file.name);
|
|
139
|
+
const progressCallback = (e) => {
|
|
140
|
+
const progress = e.loadedBytes / file.size * 100;
|
|
141
|
+
onProgress && onProgress(Math.round(progress));
|
|
45
142
|
};
|
|
143
|
+
await blockBlobClient.upload(file, file.size, { onProgress: progressCallback });
|
|
144
|
+
}
|
|
145
|
+
var upload_default = upload;
|
|
146
|
+
|
|
147
|
+
// src/modules/http.ts
|
|
148
|
+
var baseUrl = "https://iaris.easyproctor.tech/api";
|
|
149
|
+
async function makeRequest(data) {
|
|
150
|
+
var _a;
|
|
151
|
+
const { url, method, body, jwt } = data;
|
|
152
|
+
const resp = await fetch(baseUrl + url, {
|
|
153
|
+
method,
|
|
154
|
+
body: body != null ? JSON.stringify(body) : void 0,
|
|
155
|
+
headers: {
|
|
156
|
+
"Authorization": `Bearer ${jwt}`,
|
|
157
|
+
"Content-Type": "application/json"
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
if (resp.status >= 400) {
|
|
161
|
+
throw "N\xE3o foi poss\xEDvel realizar a requisi\xE7\xE3o, tente novamente mais tarde";
|
|
162
|
+
}
|
|
163
|
+
const isJson = (_a = resp.headers.get("content-type")) == null ? void 0 : _a.includes("application/json");
|
|
164
|
+
const responseData = isJson ? await resp.json() : null;
|
|
165
|
+
return responseData;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/index.ts
|
|
169
|
+
var azureBlobUrl = "https://iarisprod.azureedge.net/iaris";
|
|
170
|
+
function useProctoring(proctoringOptions) {
|
|
171
|
+
["examId", "clientId", "token"].forEach((el) => {
|
|
172
|
+
const key = el;
|
|
173
|
+
if (!proctoringOptions[key]) {
|
|
174
|
+
throw `O campo ${key} \xE9 obrigat\xF3rio`;
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
let focusCallback;
|
|
178
|
+
let lostFocusCallback;
|
|
179
|
+
if (!navigator.mediaDevices.getDisplayMedia) {
|
|
180
|
+
throw "Voc\xEA est\xE1 utilizando uma vers\xE3o muito antiga do navegador, por favor, atualize a vers\xE3o";
|
|
181
|
+
}
|
|
182
|
+
if (!window.indexedDB) {
|
|
183
|
+
throw "Voc\xEA est\xE1 usando uma vers\xE3o muito antiga do navegador, n\xE3o \xE9 poss\xEDvel relizar a requisi\xE7\xE3o";
|
|
184
|
+
}
|
|
185
|
+
function download(file) {
|
|
186
|
+
const url = URL.createObjectURL(file);
|
|
187
|
+
const a = document.createElement("a");
|
|
188
|
+
document.body.appendChild(a);
|
|
189
|
+
a.style.display = "none";
|
|
190
|
+
a.href = url;
|
|
191
|
+
a.download = file.name;
|
|
192
|
+
a.click();
|
|
193
|
+
window.URL.revokeObjectURL(url);
|
|
194
|
+
}
|
|
195
|
+
let cameraBuffer = [];
|
|
196
|
+
let screenBuffer = [];
|
|
197
|
+
let proctoringId = "";
|
|
198
|
+
let cancelCallback = null;
|
|
199
|
+
async function _startCapture() {
|
|
200
|
+
if (!document.body) {
|
|
201
|
+
throw "A execu\xE7\xE3o do script deve ser feita por algum elemento dentro do <body> da p\xE1gina html";
|
|
202
|
+
}
|
|
203
|
+
if (cancelCallback != null) {
|
|
204
|
+
throw "Uma grava\xE7\xE3o ja est\xE1 em andamento";
|
|
205
|
+
}
|
|
206
|
+
let cancelCameraCapture = null;
|
|
207
|
+
let cancelScreenCapture = null;
|
|
208
|
+
try {
|
|
209
|
+
if (focusCallback)
|
|
210
|
+
window.removeEventListener("focus", focusCallback);
|
|
211
|
+
if (lostFocusCallback)
|
|
212
|
+
window.removeEventListener("blur", lostFocusCallback);
|
|
213
|
+
cancelScreenCapture = await startScreenCapture(screenBuffer);
|
|
214
|
+
if (focusCallback)
|
|
215
|
+
window.addEventListener("focus", () => window.addEventListener("focus", focusCallback), { once: true });
|
|
216
|
+
if (lostFocusCallback)
|
|
217
|
+
window.addEventListener("blur", lostFocusCallback);
|
|
218
|
+
cancelCameraCapture = await startCameraCapture(cameraBuffer);
|
|
219
|
+
cancelCallback = async function() {
|
|
220
|
+
await Promise.all([cancelCameraCapture(), cancelScreenCapture()]);
|
|
221
|
+
};
|
|
222
|
+
} catch (error) {
|
|
223
|
+
cancelCallback = null;
|
|
224
|
+
cancelScreenCapture && await cancelScreenCapture();
|
|
225
|
+
cancelCameraCapture && await cancelCameraCapture();
|
|
226
|
+
cameraBuffer = [];
|
|
227
|
+
screenBuffer = [];
|
|
228
|
+
throw "N\xE3o foi poss\xEDvel iniciar a captura, por favor, verifique as permiss\xF5es de camera e microfone";
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
async function start(options = { override: false }) {
|
|
232
|
+
const { override } = options;
|
|
233
|
+
if (override) {
|
|
234
|
+
await Promise.all([clearBuffers("cameraBuffers"), clearBuffers("screenBuffers")]);
|
|
235
|
+
} else {
|
|
236
|
+
const [storedCameraBuffers, storedScreenBuffers] = await Promise.all([getBuffers("cameraBuffers"), getBuffers("screenBuffers")]);
|
|
237
|
+
if (storedCameraBuffers.length > 0 || storedScreenBuffers.length > 0) {
|
|
238
|
+
throw "Existe uma grava\xE7\xE3o iniciada, por favor, execute o m\xE9todo resume() para retomar, ou utilize o parametro start({ override: true }) para limpar os dados";
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
await _startCapture();
|
|
242
|
+
try {
|
|
243
|
+
const resp = await makeRequest({
|
|
244
|
+
url: `/proctoring/start/${proctoringOptions.examId}`,
|
|
245
|
+
method: "POST",
|
|
246
|
+
body: { clientId: proctoringOptions.clientId },
|
|
247
|
+
jwt: proctoringOptions.token
|
|
248
|
+
});
|
|
249
|
+
proctoringId = resp.id;
|
|
250
|
+
return resp;
|
|
251
|
+
} catch (error) {
|
|
252
|
+
cancelCallback && cancelCallback();
|
|
253
|
+
throw error;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async function pause() {
|
|
257
|
+
throw "Ainda n\xE3o implementado";
|
|
258
|
+
}
|
|
259
|
+
async function resume() {
|
|
260
|
+
throw "Ainda n\xE3o implementado";
|
|
261
|
+
}
|
|
262
|
+
async function finish(options = {}) {
|
|
263
|
+
const { onProgress } = options;
|
|
264
|
+
if (cancelCallback) {
|
|
265
|
+
await cancelCallback();
|
|
266
|
+
}
|
|
267
|
+
const time = new Date().toISOString();
|
|
268
|
+
const finalCameraBuffer = cameraBuffer;
|
|
269
|
+
const finalScreenBuffer = screenBuffer;
|
|
270
|
+
if (finalCameraBuffer.length == 0 || finalScreenBuffer.length == 0) {
|
|
271
|
+
throw "N\xE3o existe nenhuma grava\xE7\xE3o iniciada";
|
|
272
|
+
}
|
|
273
|
+
const cameraFileName = `EP_${proctoringOptions.examId}_${time}_camera_0.webm`;
|
|
274
|
+
const screenFIleName = `EP_${proctoringOptions.examId}_${time}_screen_0.webm`;
|
|
275
|
+
const cameraFile = new File(finalCameraBuffer, cameraFileName, { type: "video/webm" });
|
|
276
|
+
const screenFile = new File(finalScreenBuffer, screenFIleName, { type: "video/webm" });
|
|
277
|
+
let cameraProgress = 0;
|
|
278
|
+
const screenProgress = 0;
|
|
279
|
+
const handleOnProgress = () => {
|
|
280
|
+
onProgress && onProgress((cameraProgress + screenProgress) / 2);
|
|
281
|
+
};
|
|
282
|
+
await Promise.all([
|
|
283
|
+
upload_default({ file: cameraFile, onProgress: (progress) => {
|
|
284
|
+
cameraProgress = progress;
|
|
285
|
+
handleOnProgress();
|
|
286
|
+
} }),
|
|
287
|
+
upload_default({ file: screenFile, onProgress: (progress) => {
|
|
288
|
+
cameraProgress = progress;
|
|
289
|
+
handleOnProgress();
|
|
290
|
+
} })
|
|
291
|
+
]);
|
|
292
|
+
await makeRequest({
|
|
293
|
+
url: "/proctoring/save-screen",
|
|
294
|
+
method: "POST",
|
|
295
|
+
jwt: proctoringOptions.token,
|
|
296
|
+
body: {
|
|
297
|
+
proctoringId,
|
|
298
|
+
alerts: []
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
await makeRequest({
|
|
302
|
+
url: `/proctoring/finish/${proctoringOptions.examId}`,
|
|
303
|
+
method: "POST",
|
|
304
|
+
body: {
|
|
305
|
+
endDate: time,
|
|
306
|
+
videoCameraUrl: `${azureBlobUrl}/${cameraFileName}`,
|
|
307
|
+
videoScreenUrl: `${azureBlobUrl}/${screenFIleName}`
|
|
308
|
+
},
|
|
309
|
+
jwt: proctoringOptions.token
|
|
310
|
+
});
|
|
311
|
+
await Promise.all([clearBuffers("cameraBuffers"), clearBuffers("screenBuffers")]);
|
|
312
|
+
cameraBuffer = [];
|
|
313
|
+
screenBuffer = [];
|
|
314
|
+
cancelCallback = null;
|
|
315
|
+
}
|
|
46
316
|
const onLostFocus = (cb) => {
|
|
317
|
+
lostFocusCallback = cb;
|
|
318
|
+
window.addEventListener("blur", lostFocusCallback);
|
|
319
|
+
const dispose = () => window.removeEventListener("blur", lostFocusCallback);
|
|
320
|
+
return dispose;
|
|
321
|
+
};
|
|
322
|
+
const onReturnFocus = (cb) => {
|
|
323
|
+
focusCallback = cb;
|
|
324
|
+
window.addEventListener("focus", focusCallback);
|
|
325
|
+
const dispose = () => window.removeEventListener("focus", focusCallback);
|
|
326
|
+
return dispose;
|
|
47
327
|
};
|
|
48
|
-
return { start, finish, pause, resume, onLostFocus };
|
|
328
|
+
return { start, finish, pause, resume, onLostFocus, download, onReturnFocus };
|
|
49
329
|
}
|
|
50
330
|
if (typeof window !== "undefined") {
|
|
51
331
|
window.useProctoring = useProctoring;
|
|
52
332
|
}
|
|
53
|
-
module.exports = __toCommonJS(src_exports);
|
|
54
333
|
// Annotate the CommonJS export names for ESM import in node:
|
|
55
334
|
0 && (module.exports = {
|
|
56
335
|
useProctoring
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
declare type Tables = "cameraBuffers" | "screenBuffers";
|
|
2
|
+
export declare function initializeDatabase(table: string): Promise<IDBObjectStore>;
|
|
3
|
+
export declare function insertBuffer(table: Tables, buffer: Blob[]): Promise<void>;
|
|
4
|
+
export declare function getBuffers(table: Tables): Promise<Blob[]>;
|
|
5
|
+
export declare function clearBuffers(table: Tables): Promise<void>;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function recorder(stream: MediaStream, buffer: Blob[]): () => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function startCameraCapture(): void
|
|
1
|
+
export default function startCameraCapture(buffer: Blob[]): Promise<() => Promise<void>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function startScreenCapture(): Promise<void
|
|
1
|
+
export default function startScreenCapture(buffer: Blob[]): Promise<() => Promise<void>>;
|
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "easyproctor",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"description": "Modulo web de gravação do EasyProctor",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"module": "./esm/index.js",
|
|
7
7
|
"unpkg": "./unpkg/easyproctor.min.js",
|
|
8
8
|
"types": "./index.d.ts",
|
|
9
|
-
"keywords": [
|
|
10
|
-
|
|
9
|
+
"keywords": [
|
|
10
|
+
"proctoring"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://vsoft1.visualstudio.com/DefaultCollection/Iaris/_git/easyproctor-proctoring-componentJs",
|
|
11
13
|
"repository": {
|
|
12
14
|
"type": "git",
|
|
13
|
-
"url": "
|
|
15
|
+
"url": "https://vsoft1.visualstudio.com/DefaultCollection/Iaris/_git/easyproctor-proctoring-componentJs"
|
|
14
16
|
},
|
|
15
17
|
"scripts": {
|
|
16
18
|
"build": "node scripts/build.js && tsc",
|
|
@@ -19,10 +21,15 @@
|
|
|
19
21
|
"author": "Igor Dantas",
|
|
20
22
|
"license": "ISC",
|
|
21
23
|
"devDependencies": {
|
|
22
|
-
"@
|
|
23
|
-
"@typescript-eslint/
|
|
24
|
+
"@types/dom-mediacapture-record": "^1.0.11",
|
|
25
|
+
"@typescript-eslint/eslint-plugin": "^5.8.0",
|
|
26
|
+
"@typescript-eslint/parser": "^5.8.0",
|
|
24
27
|
"esbuild": "^0.13.13",
|
|
28
|
+
"esbuild-serve": "^1.0.1",
|
|
25
29
|
"eslint": "^8.2.0",
|
|
26
|
-
"typescript": "^
|
|
30
|
+
"typescript": "^4.5.4"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@azure/storage-blob": "^12.8.0"
|
|
27
34
|
}
|
|
28
35
|
}
|