@slates/provider 1.0.0-rc.18 → 1.0.0-rc.19
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 +328 -41
- package/dist/index.d.cts +113 -22
- package/dist/index.d.ts +113 -22
- package/dist/index.module.js +320 -41
- package/package.json +2 -1
- package/src/action/attachment.ts +55 -9
- package/src/action/directUpload.ts +59 -0
- package/src/action/getFileUrlTool.ts +41 -0
- package/src/action/index.ts +1 -0
- package/src/auth/index.ts +1 -0
- package/src/auth/redact.test.ts +103 -0
- package/src/auth/redact.ts +111 -0
- package/src/auth/types.ts +0 -1
- package/src/axios/trace.ts +69 -35
- package/src/context/context.ts +193 -3
- package/src/index.ts +1 -0
package/src/context/context.ts
CHANGED
|
@@ -1,17 +1,98 @@
|
|
|
1
|
+
import { Readable } from 'node:stream';
|
|
2
|
+
import PQueue from 'p-queue';
|
|
3
|
+
import {
|
|
4
|
+
createBase64Attachment,
|
|
5
|
+
createUrlAttachment,
|
|
6
|
+
type SlateAttachment
|
|
7
|
+
} from '../action/attachment';
|
|
8
|
+
import { type SlateLiveInvocationInfo, uploadAttachmentDirect } from '../action/directUpload';
|
|
1
9
|
import type { SlateHttpTrace } from '../axios/trace';
|
|
2
10
|
import type { SlateLogger, SlateLogMessageInput } from '../logger';
|
|
3
11
|
import type { SlateSpecification } from '../specification/specification';
|
|
4
12
|
|
|
13
|
+
export type { SlateLiveInvocationInfo };
|
|
14
|
+
|
|
15
|
+
export type SlateAddAttachmentContent =
|
|
16
|
+
| Buffer
|
|
17
|
+
| Uint8Array
|
|
18
|
+
| ArrayBuffer
|
|
19
|
+
| ReadableStream<Uint8Array>
|
|
20
|
+
| NodeJS.ReadableStream
|
|
21
|
+
| Response;
|
|
22
|
+
|
|
23
|
+
export type SlateAddAttachmentInput =
|
|
24
|
+
| {
|
|
25
|
+
type: 'url';
|
|
26
|
+
url: string | URL;
|
|
27
|
+
mimeType?: string;
|
|
28
|
+
filename?: string;
|
|
29
|
+
headers?: Record<string, string>;
|
|
30
|
+
query?: Record<string, string>;
|
|
31
|
+
refreshReference?: unknown;
|
|
32
|
+
refreshAt?: string;
|
|
33
|
+
}
|
|
34
|
+
| {
|
|
35
|
+
type: 'content';
|
|
36
|
+
content: SlateAddAttachmentContent;
|
|
37
|
+
mimeType?: string;
|
|
38
|
+
filename?: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
let isBufferLike = (value: unknown): value is Buffer | Uint8Array | ArrayBuffer =>
|
|
42
|
+
value instanceof Uint8Array || value instanceof ArrayBuffer;
|
|
43
|
+
|
|
44
|
+
let toUint8Array = (value: Buffer | Uint8Array | ArrayBuffer) =>
|
|
45
|
+
value instanceof ArrayBuffer ? new Uint8Array(value) : value;
|
|
46
|
+
|
|
47
|
+
let isReadableStream = (value: unknown): value is ReadableStream<Uint8Array> =>
|
|
48
|
+
typeof ReadableStream !== 'undefined' && value instanceof ReadableStream;
|
|
49
|
+
|
|
50
|
+
let isNodeReadable = (value: unknown): value is NodeJS.ReadableStream =>
|
|
51
|
+
!!value &&
|
|
52
|
+
typeof value === 'object' &&
|
|
53
|
+
typeof (value as any).pipe === 'function' &&
|
|
54
|
+
typeof (value as any).on === 'function';
|
|
55
|
+
|
|
56
|
+
let isResponseLike = (value: unknown): value is Response =>
|
|
57
|
+
typeof Response !== 'undefined' && value instanceof Response;
|
|
58
|
+
|
|
59
|
+
let readStreamToBuffer = async (stream: ReadableStream<Uint8Array>): Promise<Buffer> => {
|
|
60
|
+
let reader = stream.getReader();
|
|
61
|
+
let chunks: Uint8Array[] = [];
|
|
62
|
+
|
|
63
|
+
while (true) {
|
|
64
|
+
let { done, value } = await reader.read();
|
|
65
|
+
if (done) break;
|
|
66
|
+
if (value) chunks.push(value);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return Buffer.concat(chunks);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
interface NormalizedStreamInput {
|
|
73
|
+
stream: ReadableStream<Uint8Array>;
|
|
74
|
+
mimeType?: string;
|
|
75
|
+
filename?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
5
78
|
export class SlatePublicContext<InputType extends {}> {
|
|
6
79
|
#input: InputType;
|
|
7
80
|
#httpTraces: SlateHttpTrace[] = [];
|
|
8
81
|
|
|
82
|
+
#liveInvocation: SlateLiveInvocationInfo | null;
|
|
83
|
+
#attachmentsDisabled = false;
|
|
84
|
+
#attachments: SlateAttachment[] = [];
|
|
85
|
+
#pendingUploads: Promise<void>[] = [];
|
|
86
|
+
#uploadQueue = new PQueue({ concurrency: 10 });
|
|
87
|
+
|
|
9
88
|
constructor(
|
|
10
89
|
input: InputType,
|
|
11
90
|
private readonly spec: SlateSpecification<any, any>,
|
|
12
|
-
private readonly logger: SlateLogger
|
|
91
|
+
private readonly logger: SlateLogger,
|
|
92
|
+
liveInvocation: SlateLiveInvocationInfo | null = null
|
|
13
93
|
) {
|
|
14
94
|
this.#input = input;
|
|
95
|
+
this.#liveInvocation = liveInvocation;
|
|
15
96
|
}
|
|
16
97
|
|
|
17
98
|
get specification() {
|
|
@@ -86,6 +167,110 @@ export class SlatePublicContext<InputType extends {}> {
|
|
|
86
167
|
progress(message: SlateLogMessageInput) {
|
|
87
168
|
this.logger.progress(message);
|
|
88
169
|
}
|
|
170
|
+
|
|
171
|
+
async addAttachment(input: SlateAddAttachmentInput): Promise<void> {
|
|
172
|
+
if (input.type === 'url') {
|
|
173
|
+
this.#attachments.push(
|
|
174
|
+
createUrlAttachment(input.url.toString(), {
|
|
175
|
+
mimeType: input.mimeType,
|
|
176
|
+
headers: input.headers,
|
|
177
|
+
query: input.query,
|
|
178
|
+
refreshReference: input.refreshReference,
|
|
179
|
+
refreshAt: input.refreshAt
|
|
180
|
+
})
|
|
181
|
+
);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let content = input.content;
|
|
186
|
+
|
|
187
|
+
if (isBufferLike(content)) {
|
|
188
|
+
let bytes = toUint8Array(content);
|
|
189
|
+
this.#attachments.push(
|
|
190
|
+
createBase64Attachment(Buffer.from(bytes).toString('base64'), input.mimeType)
|
|
191
|
+
);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let normalized = await this.#normalizeStreamInput(content, {
|
|
196
|
+
mimeType: input.mimeType,
|
|
197
|
+
filename: input.filename
|
|
198
|
+
});
|
|
199
|
+
if (!normalized) return;
|
|
200
|
+
|
|
201
|
+
if (!this.#liveInvocation || this.#attachmentsDisabled) {
|
|
202
|
+
let buffer = await readStreamToBuffer(normalized.stream);
|
|
203
|
+
this.#attachments.push(
|
|
204
|
+
createBase64Attachment(buffer.toString('base64'), normalized.mimeType)
|
|
205
|
+
);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let live = this.#liveInvocation;
|
|
210
|
+
let task = this.#uploadQueue
|
|
211
|
+
.add(() =>
|
|
212
|
+
uploadAttachmentDirect({
|
|
213
|
+
live,
|
|
214
|
+
mimeType: normalized.mimeType,
|
|
215
|
+
filename: normalized.filename,
|
|
216
|
+
body: normalized.stream
|
|
217
|
+
})
|
|
218
|
+
)
|
|
219
|
+
.then(attachment => {
|
|
220
|
+
this.#attachments.push(attachment);
|
|
221
|
+
})
|
|
222
|
+
.catch(err => {
|
|
223
|
+
this.#attachmentsDisabled = true;
|
|
224
|
+
this.warn({
|
|
225
|
+
message: `Attachment dropped: direct upload failed (${
|
|
226
|
+
err instanceof Error ? err.message : String(err)
|
|
227
|
+
}).`
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
this.#pendingUploads.push(task);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async #normalizeStreamInput(
|
|
235
|
+
input: ReadableStream<Uint8Array> | NodeJS.ReadableStream | Response,
|
|
236
|
+
opts: { mimeType?: string; filename?: string }
|
|
237
|
+
): Promise<NormalizedStreamInput | null> {
|
|
238
|
+
if (isResponseLike(input)) {
|
|
239
|
+
if (!input.body) {
|
|
240
|
+
this.warn({ message: 'Attachment dropped: response has no body.' });
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
stream: input.body,
|
|
245
|
+
mimeType: opts.mimeType ?? input.headers.get('content-type') ?? undefined,
|
|
246
|
+
filename: opts.filename
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (isReadableStream(input)) {
|
|
251
|
+
return { stream: input, mimeType: opts.mimeType, filename: opts.filename };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (isNodeReadable(input)) {
|
|
255
|
+
return {
|
|
256
|
+
stream: Readable.toWeb(input as any) as unknown as ReadableStream<Uint8Array>,
|
|
257
|
+
mimeType: opts.mimeType,
|
|
258
|
+
filename: opts.filename
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
this.warn({ message: 'Attachment dropped: unsupported attachment input type.' });
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async _finalizeAttachments(): Promise<SlateAttachment[]> {
|
|
267
|
+
await Promise.allSettled(this.#pendingUploads);
|
|
268
|
+
return this.#attachments;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
_getAuthConfigForRedaction(): Record<string, unknown> | undefined {
|
|
272
|
+
return undefined;
|
|
273
|
+
}
|
|
89
274
|
}
|
|
90
275
|
|
|
91
276
|
export class SlateContext<
|
|
@@ -101,9 +286,10 @@ export class SlateContext<
|
|
|
101
286
|
input: InputType,
|
|
102
287
|
auth: AuthType,
|
|
103
288
|
spec: SlateSpecification<ConfigType, AuthType>,
|
|
104
|
-
logger: SlateLogger
|
|
289
|
+
logger: SlateLogger,
|
|
290
|
+
liveInvocation: SlateLiveInvocationInfo | null = null
|
|
105
291
|
) {
|
|
106
|
-
super(input, spec, logger);
|
|
292
|
+
super(input, spec, logger, liveInvocation);
|
|
107
293
|
this.#config = config;
|
|
108
294
|
this.#auth = auth;
|
|
109
295
|
}
|
|
@@ -115,4 +301,8 @@ export class SlateContext<
|
|
|
115
301
|
get auth() {
|
|
116
302
|
return Object.freeze(this.#auth);
|
|
117
303
|
}
|
|
304
|
+
|
|
305
|
+
override _getAuthConfigForRedaction(): Record<string, unknown> | undefined {
|
|
306
|
+
return this.#auth as Record<string, unknown>;
|
|
307
|
+
}
|
|
118
308
|
}
|