@nextclaw/ncp-react 0.3.5 → 0.3.6

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.d.ts CHANGED
@@ -34,13 +34,17 @@ declare function useNcpAgent(sessionId: string, client: NcpAgentClientEndpoint):
34
34
 
35
35
  declare const DEFAULT_NCP_IMAGE_ATTACHMENT_ACCEPT = "image/png,image/jpeg,image/webp,image/gif";
36
36
  declare const DEFAULT_NCP_IMAGE_ATTACHMENT_MIME_TYPES: readonly ["image/png", "image/jpeg", "image/webp", "image/gif"];
37
+ declare const DEFAULT_NCP_ATTACHMENT_MAX_BYTES: number;
37
38
  declare const DEFAULT_NCP_IMAGE_ATTACHMENT_MAX_BYTES: number;
39
+ declare const DEFAULT_NCP_FALLBACK_ATTACHMENT_MIME_TYPE = "application/octet-stream";
38
40
  type NcpDraftAttachment = {
39
41
  id: string;
40
42
  name: string;
41
43
  mimeType: string;
42
- contentBase64: string;
43
44
  sizeBytes: number;
45
+ attachmentUri?: string;
46
+ url?: string;
47
+ contentBase64?: string;
44
48
  };
45
49
  type NcpRejectedAttachment = {
46
50
  fileName: string;
@@ -52,6 +56,9 @@ type ReadNcpDraftAttachmentsOptions = {
52
56
  acceptedMimeTypes?: readonly string[];
53
57
  maxBytes?: number;
54
58
  };
59
+ type UploadNcpDraftAttachmentsOptions = ReadNcpDraftAttachmentsOptions & {
60
+ uploadBatch: (files: File[]) => Promise<NcpDraftAttachment[]>;
61
+ };
55
62
  type ReadNcpDraftAttachmentsResult = {
56
63
  attachments: NcpDraftAttachment[];
57
64
  rejected: NcpRejectedAttachment[];
@@ -67,5 +74,6 @@ declare function buildNcpRequestEnvelope(params: {
67
74
  timestamp?: string;
68
75
  }): NcpRequestEnvelope | null;
69
76
  declare function readFilesAsNcpDraftAttachments(files: Iterable<File>, options?: ReadNcpDraftAttachmentsOptions): Promise<ReadNcpDraftAttachmentsResult>;
77
+ declare function uploadFilesAsNcpDraftAttachments(files: Iterable<File>, options: UploadNcpDraftAttachmentsOptions): Promise<ReadNcpDraftAttachmentsResult>;
70
78
 
71
- export { DEFAULT_NCP_IMAGE_ATTACHMENT_ACCEPT, DEFAULT_NCP_IMAGE_ATTACHMENT_MAX_BYTES, DEFAULT_NCP_IMAGE_ATTACHMENT_MIME_TYPES, type NcpConversationSeed, type NcpConversationSeedLoader, type NcpDraftAttachment, type NcpRejectedAttachment, type ReadNcpDraftAttachmentsOptions, type ReadNcpDraftAttachmentsResult, type UseHydratedNcpAgentOptions, type UseHydratedNcpAgentResult, type UseNcpAgentResult, buildNcpImageAttachmentDataUrl, buildNcpRequestEnvelope, readFilesAsNcpDraftAttachments, useHydratedNcpAgent, useNcpAgent };
79
+ export { DEFAULT_NCP_ATTACHMENT_MAX_BYTES, DEFAULT_NCP_FALLBACK_ATTACHMENT_MIME_TYPE, DEFAULT_NCP_IMAGE_ATTACHMENT_ACCEPT, DEFAULT_NCP_IMAGE_ATTACHMENT_MAX_BYTES, DEFAULT_NCP_IMAGE_ATTACHMENT_MIME_TYPES, type NcpConversationSeed, type NcpConversationSeedLoader, type NcpDraftAttachment, type NcpRejectedAttachment, type ReadNcpDraftAttachmentsOptions, type ReadNcpDraftAttachmentsResult, type UploadNcpDraftAttachmentsOptions, type UseHydratedNcpAgentOptions, type UseHydratedNcpAgentResult, type UseNcpAgentResult, buildNcpImageAttachmentDataUrl, buildNcpRequestEnvelope, readFilesAsNcpDraftAttachments, uploadFilesAsNcpDraftAttachments, useHydratedNcpAgent, useNcpAgent };
package/dist/index.js CHANGED
@@ -233,7 +233,9 @@ var DEFAULT_NCP_IMAGE_ATTACHMENT_MIME_TYPES = [
233
233
  "image/webp",
234
234
  "image/gif"
235
235
  ];
236
- var DEFAULT_NCP_IMAGE_ATTACHMENT_MAX_BYTES = 10 * 1024 * 1024;
236
+ var DEFAULT_NCP_ATTACHMENT_MAX_BYTES = 10 * 1024 * 1024;
237
+ var DEFAULT_NCP_IMAGE_ATTACHMENT_MAX_BYTES = DEFAULT_NCP_ATTACHMENT_MAX_BYTES;
238
+ var DEFAULT_NCP_FALLBACK_ATTACHMENT_MIME_TYPE = "application/octet-stream";
237
239
  function createAttachmentId() {
238
240
  return `ncp-file-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
239
241
  }
@@ -255,7 +257,45 @@ function toBase64Content(dataUrl) {
255
257
  const commaIndex = dataUrl.indexOf(",");
256
258
  return commaIndex >= 0 ? dataUrl.slice(commaIndex + 1) : dataUrl;
257
259
  }
260
+ function normalizeAttachmentMimeType(file) {
261
+ const mimeType = file.type.trim().toLowerCase();
262
+ return mimeType.length > 0 ? mimeType : DEFAULT_NCP_FALLBACK_ATTACHMENT_MIME_TYPE;
263
+ }
264
+ function validateAttachmentFile(file, options) {
265
+ const acceptedMimeTypes = options.acceptedMimeTypes && options.acceptedMimeTypes.length > 0 ? new Set(options.acceptedMimeTypes.map((mimeType2) => mimeType2.trim().toLowerCase())) : null;
266
+ const maxBytes = options.maxBytes ?? DEFAULT_NCP_ATTACHMENT_MAX_BYTES;
267
+ const mimeType = normalizeAttachmentMimeType(file);
268
+ if (acceptedMimeTypes && !acceptedMimeTypes.has(mimeType)) {
269
+ return {
270
+ ok: false,
271
+ rejected: {
272
+ fileName: file.name,
273
+ mimeType,
274
+ sizeBytes: file.size,
275
+ reason: "unsupported-type"
276
+ }
277
+ };
278
+ }
279
+ if (file.size > maxBytes) {
280
+ return {
281
+ ok: false,
282
+ rejected: {
283
+ fileName: file.name,
284
+ mimeType,
285
+ sizeBytes: file.size,
286
+ reason: "too-large"
287
+ }
288
+ };
289
+ }
290
+ return { ok: true, mimeType };
291
+ }
258
292
  function buildNcpImageAttachmentDataUrl(attachment) {
293
+ if (attachment.url?.trim()) {
294
+ return attachment.url.trim();
295
+ }
296
+ if (!attachment.contentBase64?.trim()) {
297
+ throw new Error(`Attachment ${attachment.name} does not have image content.`);
298
+ }
259
299
  return `data:${attachment.mimeType};base64,${attachment.contentBase64}`;
260
300
  }
261
301
  function buildNcpRequestEnvelope(params) {
@@ -265,7 +305,9 @@ function buildNcpRequestEnvelope(params) {
265
305
  type: "file",
266
306
  name: attachment.name,
267
307
  mimeType: attachment.mimeType,
268
- contentBase64: attachment.contentBase64,
308
+ ...attachment.attachmentUri?.trim() ? { attachmentUri: attachment.attachmentUri.trim() } : {},
309
+ ...attachment.url?.trim() ? { url: attachment.url.trim() } : {},
310
+ ...attachment.contentBase64?.trim() ? { contentBase64: attachment.contentBase64.trim() } : {},
269
311
  sizeBytes: attachment.sizeBytes
270
312
  }))
271
313
  ];
@@ -289,30 +331,12 @@ function buildNcpRequestEnvelope(params) {
289
331
  };
290
332
  }
291
333
  async function readFilesAsNcpDraftAttachments(files, options = {}) {
292
- const acceptedMimeTypes = new Set(
293
- options.acceptedMimeTypes ?? DEFAULT_NCP_IMAGE_ATTACHMENT_MIME_TYPES
294
- );
295
- const maxBytes = options.maxBytes ?? DEFAULT_NCP_IMAGE_ATTACHMENT_MAX_BYTES;
296
334
  const attachments = [];
297
335
  const rejected = [];
298
336
  for (const file of files) {
299
- const mimeType = file.type.trim().toLowerCase();
300
- if (!acceptedMimeTypes.has(mimeType)) {
301
- rejected.push({
302
- fileName: file.name,
303
- mimeType,
304
- sizeBytes: file.size,
305
- reason: "unsupported-type"
306
- });
307
- continue;
308
- }
309
- if (file.size > maxBytes) {
310
- rejected.push({
311
- fileName: file.name,
312
- mimeType,
313
- sizeBytes: file.size,
314
- reason: "too-large"
315
- });
337
+ const validation = validateAttachmentFile(file, options);
338
+ if (!validation.ok) {
339
+ rejected.push(validation.rejected);
316
340
  continue;
317
341
  }
318
342
  try {
@@ -320,14 +344,14 @@ async function readFilesAsNcpDraftAttachments(files, options = {}) {
320
344
  attachments.push({
321
345
  id: createAttachmentId(),
322
346
  name: file.name,
323
- mimeType,
347
+ mimeType: validation.mimeType,
324
348
  contentBase64: toBase64Content(dataUrl),
325
349
  sizeBytes: file.size
326
350
  });
327
351
  } catch {
328
352
  rejected.push({
329
353
  fileName: file.name,
330
- mimeType,
354
+ mimeType: validation.mimeType,
331
355
  sizeBytes: file.size,
332
356
  reason: "read-failed"
333
357
  });
@@ -335,13 +359,51 @@ async function readFilesAsNcpDraftAttachments(files, options = {}) {
335
359
  }
336
360
  return { attachments, rejected };
337
361
  }
362
+ async function uploadFilesAsNcpDraftAttachments(files, options) {
363
+ const validFiles = [];
364
+ const rejected = [];
365
+ for (const file of files) {
366
+ const validation = validateAttachmentFile(file, options);
367
+ if (!validation.ok) {
368
+ rejected.push(validation.rejected);
369
+ continue;
370
+ }
371
+ validFiles.push(file);
372
+ }
373
+ if (validFiles.length === 0) {
374
+ return { attachments: [], rejected };
375
+ }
376
+ try {
377
+ const attachments = await options.uploadBatch(validFiles);
378
+ return {
379
+ attachments,
380
+ rejected
381
+ };
382
+ } catch {
383
+ rejected.push(
384
+ ...validFiles.map((file) => ({
385
+ fileName: file.name,
386
+ mimeType: normalizeAttachmentMimeType(file),
387
+ sizeBytes: file.size,
388
+ reason: "read-failed"
389
+ }))
390
+ );
391
+ return {
392
+ attachments: [],
393
+ rejected
394
+ };
395
+ }
396
+ }
338
397
  export {
398
+ DEFAULT_NCP_ATTACHMENT_MAX_BYTES,
399
+ DEFAULT_NCP_FALLBACK_ATTACHMENT_MIME_TYPE,
339
400
  DEFAULT_NCP_IMAGE_ATTACHMENT_ACCEPT,
340
401
  DEFAULT_NCP_IMAGE_ATTACHMENT_MAX_BYTES,
341
402
  DEFAULT_NCP_IMAGE_ATTACHMENT_MIME_TYPES,
342
403
  buildNcpImageAttachmentDataUrl,
343
404
  buildNcpRequestEnvelope,
344
405
  readFilesAsNcpDraftAttachments,
406
+ uploadFilesAsNcpDraftAttachments,
345
407
  useHydratedNcpAgent,
346
408
  useNcpAgent
347
409
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/ncp-react",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "private": false,
5
5
  "description": "React bindings for building NCP-based agent applications.",
6
6
  "type": "module",
@@ -15,8 +15,8 @@
15
15
  "dist"
16
16
  ],
17
17
  "dependencies": {
18
- "@nextclaw/ncp": "0.3.2",
19
- "@nextclaw/ncp-toolkit": "0.4.2"
18
+ "@nextclaw/ncp-toolkit": "0.4.3",
19
+ "@nextclaw/ncp": "0.3.3"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "react": "^18.0.0 || ^19.0.0"