simple-strapi 1.0.0-alpha.22 → 1.0.0-alpha.24

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/client.d.ts CHANGED
@@ -6,7 +6,7 @@ import z from "zod";
6
6
  import { DynamicField, DynamicOptions, InferDynamic } from "./fields/dynamic";
7
7
  import { defaultStrapiFieldsSchema } from "./utils/schema";
8
8
  import { ComponentRepeatableField, ComponentRepeatableOptions, ComponentSingleField, ComponentSingleOptions, InferComponentRepeatable, InferComponentSingle } from "./fields/component";
9
- import { InferMediaSingle, MediaSingleField, MediaSingleOptions } from "./fields/media";
9
+ import { InferMediaSingle, MediaSingleField, MediaSingleOptions, ZodMediaType } from "./fields/media";
10
10
  import { EnumerationField, EnumerationOptions, InferEnumeration } from "./fields/enumeration";
11
11
  import { InferRichTextBlocks, RichTextBlocksField, RichTextBlocksOptions } from "./fields/richText";
12
12
  import { InferJSON, JSONField, JSONOptions } from "./fields/json";
@@ -75,6 +75,7 @@ declare class Client {
75
75
  }>);
76
76
  private getAuthorizedHeaders;
77
77
  private populateFromSchema;
78
+ private resolveRef;
78
79
  getSingle<S extends Schema>(pluralID: string, options: EntityRequest<{
79
80
  schema: S;
80
81
  populate?: any;
@@ -138,5 +139,31 @@ declare class Client {
138
139
  data: any;
139
140
  meta: any;
140
141
  }>;
142
+ private getOrCreateFolder;
143
+ /**
144
+ * Carica un file sulla Media Library di Strapi.
145
+ *
146
+ * @param file - Sorgente del file: `Blob`, `File` (browser) oppure stringa base64
147
+ * (data URI `data:mime;base64,...` o raw base64).
148
+ * @param options.filename - Nome del file nel FormData. Obbligatorio per base64 e
149
+ * Blob senza nome; per `File` viene estratto automaticamente.
150
+ * @param options.ref - Nome del Content Type (es. `"product"` → `api::product.product`)
151
+ * oppure UID completo (es. `"plugin::users-permissions.user"`).
152
+ * @param options.refId - `documentId` dell'entità a cui agganciare il file.
153
+ * @param options.field - Nome del campo top-level dell'entità.
154
+ * ⚠️ I campi annidati (dot-notation) non sono supportati
155
+ * nativamente dall'endpoint `/upload` di Strapi: caricare
156
+ * il file separatamente e aggiornare l'entità con `update`.
157
+ * @param options.path - Percorso della cartella nella Media Library (es. `"products/2024"`).
158
+ * La cartella viene creata automaticamente se non esiste (mkdir -p).
159
+ */
160
+ upload(file: Blob | string, options?: {
161
+ filename?: string;
162
+ ref?: string;
163
+ refId?: string | number;
164
+ field?: string;
165
+ path?: string;
166
+ headers?: Record<string, string>;
167
+ }): Promise<ZodMediaType[]>;
141
168
  }
142
169
  export default Client;
package/dist/client.js CHANGED
@@ -4,6 +4,7 @@ import fetch from "node-fetch";
4
4
  import qs from "qs";
5
5
  import z from "zod";
6
6
  import { defaultStrapiFields, schemaToParser } from "./utils/schema";
7
+ import { zodMediaSchema } from "./fields/media";
7
8
  class Client {
8
9
  static async create(endpoint, { auth, ...options } = {}) {
9
10
  const endpointURL = new URL(endpoint);
@@ -119,6 +120,11 @@ class Client {
119
120
  }
120
121
  return populate;
121
122
  };
123
+ this.resolveRef = (ref) => {
124
+ if (ref.includes("::"))
125
+ return ref;
126
+ return `api::${ref}.${ref}`;
127
+ };
122
128
  const headers = (() => {
123
129
  return { ...Client.headers, ...(this.options.headers || {}) };
124
130
  })();
@@ -378,6 +384,164 @@ class Client {
378
384
  throw ensureSimpleException(exception);
379
385
  }
380
386
  }
387
+ /*
388
+ * ==========================================
389
+ * AUTO GENERATED - upload method
390
+ * ==========================================
391
+ */
392
+ async getOrCreateFolder(folderPath) {
393
+ const segments = folderPath.split("/").filter(Boolean);
394
+ let currentParentId = null;
395
+ for (const segment of segments) {
396
+ const params = currentParentId === null
397
+ ? { filters: { name: { $eq: segment }, parent: { $null: true } } }
398
+ : { filters: { name: { $eq: segment }, parent: { id: { $eq: currentParentId } } } };
399
+ const listURL = Client.getRequestURL({
400
+ origin: this.origin,
401
+ pathname: join(this.pathname, "upload/folders"),
402
+ params,
403
+ });
404
+ const listResponse = await fetch(listURL, {
405
+ method: "GET",
406
+ headers: this.getAuthorizedHeaders(),
407
+ });
408
+ if (!listResponse.ok) {
409
+ const errorBody = await listResponse.json().catch(() => ({}));
410
+ throw createSimpleException({
411
+ code: listResponse.status,
412
+ message: errorBody.error?.message || listResponse.statusText,
413
+ type: "error",
414
+ source: "strapi-utils/client.ts",
415
+ });
416
+ }
417
+ const { data: folders } = z
418
+ .object({ data: z.array(z.object({ id: z.number() }).loose()) })
419
+ .parse(await listResponse.json());
420
+ if (folders.length > 0) {
421
+ currentParentId = folders[0].id;
422
+ }
423
+ else {
424
+ const createURL = Client.getRequestURL({
425
+ origin: this.origin,
426
+ pathname: join(this.pathname, "upload/folders"),
427
+ params: {},
428
+ });
429
+ const createBody = currentParentId === null
430
+ ? { name: segment }
431
+ : { name: segment, parent: currentParentId };
432
+ const createResponse = await fetch(createURL, {
433
+ method: "POST",
434
+ headers: this.getAuthorizedHeaders(),
435
+ body: JSON.stringify(createBody),
436
+ });
437
+ if (!createResponse.ok) {
438
+ const errorBody = await createResponse.json().catch(() => ({}));
439
+ throw createSimpleException({
440
+ code: createResponse.status,
441
+ message: errorBody.error?.message || createResponse.statusText,
442
+ type: "error",
443
+ source: "strapi-utils/client.ts",
444
+ });
445
+ }
446
+ const { data: created } = z
447
+ .object({ data: z.object({ id: z.number() }).loose() })
448
+ .parse(await createResponse.json());
449
+ currentParentId = created.id;
450
+ }
451
+ }
452
+ return currentParentId;
453
+ }
454
+ /**
455
+ * Carica un file sulla Media Library di Strapi.
456
+ *
457
+ * @param file - Sorgente del file: `Blob`, `File` (browser) oppure stringa base64
458
+ * (data URI `data:mime;base64,...` o raw base64).
459
+ * @param options.filename - Nome del file nel FormData. Obbligatorio per base64 e
460
+ * Blob senza nome; per `File` viene estratto automaticamente.
461
+ * @param options.ref - Nome del Content Type (es. `"product"` → `api::product.product`)
462
+ * oppure UID completo (es. `"plugin::users-permissions.user"`).
463
+ * @param options.refId - `documentId` dell'entità a cui agganciare il file.
464
+ * @param options.field - Nome del campo top-level dell'entità.
465
+ * ⚠️ I campi annidati (dot-notation) non sono supportati
466
+ * nativamente dall'endpoint `/upload` di Strapi: caricare
467
+ * il file separatamente e aggiornare l'entità con `update`.
468
+ * @param options.path - Percorso della cartella nella Media Library (es. `"products/2024"`).
469
+ * La cartella viene creata automaticamente se non esiste (mkdir -p).
470
+ */
471
+ async upload(file, options = {}) {
472
+ try {
473
+ const { ref, refId, field, headers = {} } = options;
474
+ let blob;
475
+ let fileName;
476
+ if (typeof file === "string") {
477
+ let mimeType = "application/octet-stream";
478
+ let rawBase64 = file;
479
+ if (file.startsWith("data:")) {
480
+ const commaIndex = file.indexOf(",");
481
+ mimeType = file.slice(5, file.indexOf(";"));
482
+ rawBase64 = file.slice(commaIndex + 1);
483
+ }
484
+ const bytes = typeof Buffer !== "undefined"
485
+ ? new Uint8Array(Buffer.from(rawBase64, "base64"))
486
+ : (() => {
487
+ const bin = atob(rawBase64);
488
+ const arr = new Uint8Array(bin.length);
489
+ for (let i = 0; i < bin.length; i++)
490
+ arr[i] = bin.charCodeAt(i);
491
+ return arr;
492
+ })();
493
+ blob = new Blob([bytes], { type: mimeType });
494
+ fileName = options.filename ?? "upload";
495
+ }
496
+ else {
497
+ blob = file;
498
+ fileName = options.filename ?? ("name" in file ? file.name : "upload");
499
+ }
500
+ let folderId;
501
+ if (options.path) {
502
+ folderId = await this.getOrCreateFolder(options.path);
503
+ }
504
+ const formData = new FormData();
505
+ formData.append("files", blob, fileName);
506
+ if (ref !== undefined)
507
+ formData.append("ref", this.resolveRef(ref));
508
+ if (refId !== undefined)
509
+ formData.append("refId", String(refId));
510
+ if (field !== undefined)
511
+ formData.append("field", field);
512
+ if (folderId !== undefined) {
513
+ formData.append("fileInfo", JSON.stringify({ folder: folderId }));
514
+ }
515
+ const requestURL = Client.getRequestURL({
516
+ origin: this.origin,
517
+ pathname: join(this.pathname, "upload"),
518
+ params: {},
519
+ });
520
+ const { "Content-Type": _ct, ...headersWithoutContentType } = this.getAuthorizedHeaders();
521
+ const response = await fetch(requestURL, {
522
+ method: "POST",
523
+ headers: {
524
+ ...headersWithoutContentType,
525
+ ...headers,
526
+ },
527
+ body: formData,
528
+ });
529
+ if (!response.ok) {
530
+ const errorBody = await response.json().catch(() => ({}));
531
+ throw createSimpleException({
532
+ code: response.status,
533
+ message: errorBody.error?.message || response.statusText,
534
+ type: "error",
535
+ source: "strapi-utils/client.ts",
536
+ });
537
+ }
538
+ const data = await response.json();
539
+ return z.array(zodMediaSchema).parse(data);
540
+ }
541
+ catch (exception) {
542
+ throw ensureSimpleException(exception);
543
+ }
544
+ }
381
545
  }
382
546
  // #region STATIC
383
547
  Client.headers = {
@@ -6,7 +6,7 @@ export declare const zodMediaSchema: z.ZodObject<{
6
6
  caption: z.ZodNullable<z.ZodString>;
7
7
  width: z.ZodNullable<z.ZodNumber>;
8
8
  height: z.ZodNullable<z.ZodNumber>;
9
- formats: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
9
+ formats: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodObject<{
10
10
  name: z.ZodString;
11
11
  hash: z.ZodOptional<z.ZodString>;
12
12
  ext: z.ZodOptional<z.ZodString>;
@@ -16,7 +16,7 @@ export declare const zodMediaSchema: z.ZodObject<{
16
16
  url: z.ZodString;
17
17
  width: z.ZodNumber;
18
18
  height: z.ZodNumber;
19
- }, z.core.$strip>>>;
19
+ }, z.core.$strip>>>>;
20
20
  hash: z.ZodString;
21
21
  ext: z.ZodString;
22
22
  mime: z.ZodString;
@@ -28,7 +28,7 @@ export declare const zodMediaSchema: z.ZodObject<{
28
28
  createdAt: z.ZodISODateTime;
29
29
  updatedAt: z.ZodISODateTime;
30
30
  }, z.core.$strip>;
31
- type ZodMediaType = z.output<typeof zodMediaSchema>;
31
+ export type ZodMediaType = z.output<typeof zodMediaSchema>;
32
32
  export type MediaSingleOptions = {
33
33
  required?: boolean;
34
34
  };
@@ -38,4 +38,3 @@ export type MediaSingleField = readonly ["media.single", MediaSingleOptions];
38
38
  export declare const media: {
39
39
  single: <O extends MediaSingleOptions = {}>(options?: O) => ["media.single", O];
40
40
  };
41
- export {};
@@ -18,6 +18,7 @@ export const zodMediaSchema = z.object({
18
18
  width: z.number(),
19
19
  height: z.number(),
20
20
  }))
21
+ .nullable()
21
22
  .optional(),
22
23
  hash: z.string(),
23
24
  ext: z.string(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simple-strapi",
3
- "version": "1.0.0-alpha.22",
3
+ "version": "1.0.0-alpha.24",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",