@slates/provider 1.0.0-rc.20 → 1.0.0-rc.21
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/dist/index.cjs +42 -4
- package/dist/index.module.js +42 -4
- package/package.json +1 -1
- package/src/action/directUpload.test.ts +70 -0
- package/src/action/directUpload.ts +50 -4
package/dist/index.cjs
CHANGED
|
@@ -1210,8 +1210,38 @@ var createTextAttachment = (content, mimeType, attachmentHash) => ({
|
|
|
1210
1210
|
});
|
|
1211
1211
|
|
|
1212
1212
|
// src/action/directUpload.ts
|
|
1213
|
+
var bufferAttachmentBody = async (body, maxSizeBytes) => {
|
|
1214
|
+
if (body instanceof Uint8Array) {
|
|
1215
|
+
if (body.byteLength > maxSizeBytes) {
|
|
1216
|
+
throw new Error(`Attachment exceeds the maximum size of ${maxSizeBytes} bytes`);
|
|
1217
|
+
}
|
|
1218
|
+
return body;
|
|
1219
|
+
}
|
|
1220
|
+
let reader = body.getReader();
|
|
1221
|
+
let chunks = [];
|
|
1222
|
+
let sizeBytes = 0;
|
|
1223
|
+
while (true) {
|
|
1224
|
+
let { done, value } = await reader.read();
|
|
1225
|
+
if (done) break;
|
|
1226
|
+
if (!value) continue;
|
|
1227
|
+
sizeBytes += value.byteLength;
|
|
1228
|
+
if (sizeBytes > maxSizeBytes) {
|
|
1229
|
+
await reader.cancel();
|
|
1230
|
+
throw new Error(`Attachment exceeds the maximum size of ${maxSizeBytes} bytes`);
|
|
1231
|
+
}
|
|
1232
|
+
chunks.push(value);
|
|
1233
|
+
}
|
|
1234
|
+
let buffered = new Uint8Array(sizeBytes);
|
|
1235
|
+
let offset = 0;
|
|
1236
|
+
for (let chunk of chunks) {
|
|
1237
|
+
buffered.set(chunk, offset);
|
|
1238
|
+
offset += chunk.byteLength;
|
|
1239
|
+
}
|
|
1240
|
+
return buffered;
|
|
1241
|
+
};
|
|
1213
1242
|
var uploadAttachmentDirect = async (d) => {
|
|
1214
1243
|
let baseUrl = d.live.baseUrl.replace(/\/$/, "");
|
|
1244
|
+
let body = await bufferAttachmentBody(d.body, d.live.maxAttachmentSizeBytes);
|
|
1215
1245
|
let registerRes = await fetch(`${baseUrl}/slates-hub/live-invocation/attachments`, {
|
|
1216
1246
|
method: "POST",
|
|
1217
1247
|
headers: {
|
|
@@ -1219,7 +1249,13 @@ var uploadAttachmentDirect = async (d) => {
|
|
|
1219
1249
|
authorization: `Bearer ${d.live.token}`
|
|
1220
1250
|
},
|
|
1221
1251
|
body: JSON.stringify({
|
|
1222
|
-
attachments: [
|
|
1252
|
+
attachments: [
|
|
1253
|
+
{
|
|
1254
|
+
mimeType: d.mimeType,
|
|
1255
|
+
filename: d.filename,
|
|
1256
|
+
sizeBytes: body.byteLength || void 0
|
|
1257
|
+
}
|
|
1258
|
+
]
|
|
1223
1259
|
})
|
|
1224
1260
|
});
|
|
1225
1261
|
if (!registerRes.ok) {
|
|
@@ -1234,9 +1270,11 @@ var uploadAttachmentDirect = async (d) => {
|
|
|
1234
1270
|
}
|
|
1235
1271
|
let putRes = await fetch(registration.uploadUrl, {
|
|
1236
1272
|
method: "PUT",
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1273
|
+
headers: {
|
|
1274
|
+
...d.mimeType ? { "content-type": d.mimeType } : {},
|
|
1275
|
+
"content-length": String(body.byteLength)
|
|
1276
|
+
},
|
|
1277
|
+
body
|
|
1240
1278
|
});
|
|
1241
1279
|
if (!putRes.ok) {
|
|
1242
1280
|
throw new Error(`Failed to upload attachment content (status ${putRes.status})`);
|
package/dist/index.module.js
CHANGED
|
@@ -1101,8 +1101,38 @@ var createTextAttachment = (content, mimeType, attachmentHash) => ({
|
|
|
1101
1101
|
});
|
|
1102
1102
|
|
|
1103
1103
|
// src/action/directUpload.ts
|
|
1104
|
+
var bufferAttachmentBody = async (body, maxSizeBytes) => {
|
|
1105
|
+
if (body instanceof Uint8Array) {
|
|
1106
|
+
if (body.byteLength > maxSizeBytes) {
|
|
1107
|
+
throw new Error(`Attachment exceeds the maximum size of ${maxSizeBytes} bytes`);
|
|
1108
|
+
}
|
|
1109
|
+
return body;
|
|
1110
|
+
}
|
|
1111
|
+
let reader = body.getReader();
|
|
1112
|
+
let chunks = [];
|
|
1113
|
+
let sizeBytes = 0;
|
|
1114
|
+
while (true) {
|
|
1115
|
+
let { done, value } = await reader.read();
|
|
1116
|
+
if (done) break;
|
|
1117
|
+
if (!value) continue;
|
|
1118
|
+
sizeBytes += value.byteLength;
|
|
1119
|
+
if (sizeBytes > maxSizeBytes) {
|
|
1120
|
+
await reader.cancel();
|
|
1121
|
+
throw new Error(`Attachment exceeds the maximum size of ${maxSizeBytes} bytes`);
|
|
1122
|
+
}
|
|
1123
|
+
chunks.push(value);
|
|
1124
|
+
}
|
|
1125
|
+
let buffered = new Uint8Array(sizeBytes);
|
|
1126
|
+
let offset = 0;
|
|
1127
|
+
for (let chunk of chunks) {
|
|
1128
|
+
buffered.set(chunk, offset);
|
|
1129
|
+
offset += chunk.byteLength;
|
|
1130
|
+
}
|
|
1131
|
+
return buffered;
|
|
1132
|
+
};
|
|
1104
1133
|
var uploadAttachmentDirect = async (d) => {
|
|
1105
1134
|
let baseUrl = d.live.baseUrl.replace(/\/$/, "");
|
|
1135
|
+
let body = await bufferAttachmentBody(d.body, d.live.maxAttachmentSizeBytes);
|
|
1106
1136
|
let registerRes = await fetch(`${baseUrl}/slates-hub/live-invocation/attachments`, {
|
|
1107
1137
|
method: "POST",
|
|
1108
1138
|
headers: {
|
|
@@ -1110,7 +1140,13 @@ var uploadAttachmentDirect = async (d) => {
|
|
|
1110
1140
|
authorization: `Bearer ${d.live.token}`
|
|
1111
1141
|
},
|
|
1112
1142
|
body: JSON.stringify({
|
|
1113
|
-
attachments: [
|
|
1143
|
+
attachments: [
|
|
1144
|
+
{
|
|
1145
|
+
mimeType: d.mimeType,
|
|
1146
|
+
filename: d.filename,
|
|
1147
|
+
sizeBytes: body.byteLength || void 0
|
|
1148
|
+
}
|
|
1149
|
+
]
|
|
1114
1150
|
})
|
|
1115
1151
|
});
|
|
1116
1152
|
if (!registerRes.ok) {
|
|
@@ -1125,9 +1161,11 @@ var uploadAttachmentDirect = async (d) => {
|
|
|
1125
1161
|
}
|
|
1126
1162
|
let putRes = await fetch(registration.uploadUrl, {
|
|
1127
1163
|
method: "PUT",
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1164
|
+
headers: {
|
|
1165
|
+
...d.mimeType ? { "content-type": d.mimeType } : {},
|
|
1166
|
+
"content-length": String(body.byteLength)
|
|
1167
|
+
},
|
|
1168
|
+
body
|
|
1131
1169
|
});
|
|
1132
1170
|
if (!putRes.ok) {
|
|
1133
1171
|
throw new Error(`Failed to upload attachment content (status ${putRes.status})`);
|
package/package.json
CHANGED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { uploadAttachmentDirect } from './directUpload';
|
|
3
|
+
|
|
4
|
+
let live = {
|
|
5
|
+
token: 'live-token',
|
|
6
|
+
baseUrl: 'https://hub.example/',
|
|
7
|
+
maxAttachmentSizeBytes: 10
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
describe('uploadAttachmentDirect', () => {
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
vi.restoreAllMocks();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('buffers streams and uploads them with their exact content length', async () => {
|
|
16
|
+
let fetchSpy = vi
|
|
17
|
+
.spyOn(globalThis, 'fetch')
|
|
18
|
+
.mockResolvedValueOnce(
|
|
19
|
+
Response.json({
|
|
20
|
+
attachments: [
|
|
21
|
+
{ referenceId: 'attachment-reference', uploadUrl: 'https://s3.example/upload' }
|
|
22
|
+
]
|
|
23
|
+
})
|
|
24
|
+
)
|
|
25
|
+
.mockResolvedValueOnce(new Response(null, { status: 200 }));
|
|
26
|
+
let body = new ReadableStream<Uint8Array>({
|
|
27
|
+
start(controller) {
|
|
28
|
+
controller.enqueue(new Uint8Array([1, 2]));
|
|
29
|
+
controller.enqueue(new Uint8Array([3, 4, 5]));
|
|
30
|
+
controller.close();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
await expect(
|
|
35
|
+
uploadAttachmentDirect({ live, body, mimeType: 'application/octet-stream' })
|
|
36
|
+
).resolves.toEqual({
|
|
37
|
+
mimeType: 'application/octet-stream',
|
|
38
|
+
content: { type: 'upload_reference', referenceId: 'attachment-reference' }
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(JSON.parse(fetchSpy.mock.calls[0]![1]!.body as string)).toEqual({
|
|
42
|
+
attachments: [{ mimeType: 'application/octet-stream', sizeBytes: 5 }]
|
|
43
|
+
});
|
|
44
|
+
let upload = fetchSpy.mock.calls[1]!;
|
|
45
|
+
expect(upload[0]).toBe('https://s3.example/upload');
|
|
46
|
+
expect(upload[1]).toMatchObject({
|
|
47
|
+
method: 'PUT',
|
|
48
|
+
headers: {
|
|
49
|
+
'content-type': 'application/octet-stream',
|
|
50
|
+
'content-length': '5'
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
expect(upload[1]!.body).toEqual(new Uint8Array([1, 2, 3, 4, 5]));
|
|
54
|
+
expect(upload[1]).not.toHaveProperty('duplex');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('rejects an oversized stream before registering an upload', async () => {
|
|
58
|
+
let fetchSpy = vi.spyOn(globalThis, 'fetch');
|
|
59
|
+
let body = new ReadableStream<Uint8Array>({
|
|
60
|
+
start(controller) {
|
|
61
|
+
controller.enqueue(new Uint8Array(11));
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await expect(uploadAttachmentDirect({ live, body })).rejects.toThrow(
|
|
66
|
+
'Attachment exceeds the maximum size of 10 bytes'
|
|
67
|
+
);
|
|
68
|
+
expect(fetchSpy).not.toHaveBeenCalled();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -13,10 +13,48 @@ export interface SlateDirectUploadInput {
|
|
|
13
13
|
body: ReadableStream<Uint8Array> | Uint8Array;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
let bufferAttachmentBody = async (
|
|
17
|
+
body: SlateDirectUploadInput['body'],
|
|
18
|
+
maxSizeBytes: number
|
|
19
|
+
) => {
|
|
20
|
+
if (body instanceof Uint8Array) {
|
|
21
|
+
if (body.byteLength > maxSizeBytes) {
|
|
22
|
+
throw new Error(`Attachment exceeds the maximum size of ${maxSizeBytes} bytes`);
|
|
23
|
+
}
|
|
24
|
+
return body;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let reader = body.getReader();
|
|
28
|
+
let chunks: Uint8Array[] = [];
|
|
29
|
+
let sizeBytes = 0;
|
|
30
|
+
|
|
31
|
+
while (true) {
|
|
32
|
+
let { done, value } = await reader.read();
|
|
33
|
+
if (done) break;
|
|
34
|
+
if (!value) continue;
|
|
35
|
+
|
|
36
|
+
sizeBytes += value.byteLength;
|
|
37
|
+
if (sizeBytes > maxSizeBytes) {
|
|
38
|
+
await reader.cancel();
|
|
39
|
+
throw new Error(`Attachment exceeds the maximum size of ${maxSizeBytes} bytes`);
|
|
40
|
+
}
|
|
41
|
+
chunks.push(value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let buffered = new Uint8Array(sizeBytes);
|
|
45
|
+
let offset = 0;
|
|
46
|
+
for (let chunk of chunks) {
|
|
47
|
+
buffered.set(chunk, offset);
|
|
48
|
+
offset += chunk.byteLength;
|
|
49
|
+
}
|
|
50
|
+
return buffered;
|
|
51
|
+
};
|
|
52
|
+
|
|
16
53
|
export let uploadAttachmentDirect = async (
|
|
17
54
|
d: SlateDirectUploadInput
|
|
18
55
|
): Promise<SlateAttachment> => {
|
|
19
56
|
let baseUrl = d.live.baseUrl.replace(/\/$/, '');
|
|
57
|
+
let body = await bufferAttachmentBody(d.body, d.live.maxAttachmentSizeBytes);
|
|
20
58
|
|
|
21
59
|
let registerRes = await fetch(`${baseUrl}/slates-hub/live-invocation/attachments`, {
|
|
22
60
|
method: 'POST',
|
|
@@ -25,7 +63,13 @@ export let uploadAttachmentDirect = async (
|
|
|
25
63
|
authorization: `Bearer ${d.live.token}`
|
|
26
64
|
},
|
|
27
65
|
body: JSON.stringify({
|
|
28
|
-
attachments: [
|
|
66
|
+
attachments: [
|
|
67
|
+
{
|
|
68
|
+
mimeType: d.mimeType,
|
|
69
|
+
filename: d.filename,
|
|
70
|
+
sizeBytes: body.byteLength || undefined
|
|
71
|
+
}
|
|
72
|
+
]
|
|
29
73
|
})
|
|
30
74
|
});
|
|
31
75
|
if (!registerRes.ok) {
|
|
@@ -44,9 +88,11 @@ export let uploadAttachmentDirect = async (
|
|
|
44
88
|
|
|
45
89
|
let putRes = await fetch(registration.uploadUrl, {
|
|
46
90
|
method: 'PUT',
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
91
|
+
headers: {
|
|
92
|
+
...(d.mimeType ? { 'content-type': d.mimeType } : {}),
|
|
93
|
+
'content-length': String(body.byteLength)
|
|
94
|
+
},
|
|
95
|
+
body
|
|
50
96
|
});
|
|
51
97
|
if (!putRes.ok) {
|
|
52
98
|
throw new Error(`Failed to upload attachment content (status ${putRes.status})`);
|