@miguelmorales13/nestkit 0.8.0 → 0.10.0

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 CHANGED
@@ -578,6 +578,49 @@ El token se saca en developers.pinterest.com con los permisos `boards:read`,
578
578
  `pins:read` y `pins:write`. Los títulos se recortan a 100 caracteres y las
579
579
  descripciones a 800, que son los límites del API — pasarse devuelve un 400.
580
580
 
581
+ ### `tiktok` — publicar videos, y el modo que evita la auditoría
582
+
583
+ Publicar son dos llamadas: inicializar la subida y sondear hasta que TikTok
584
+ termine de procesar.
585
+
586
+ ```ts
587
+ import { TikTokModule, TikTokService } from '@miguelmorales13/nestkit/tiktok';
588
+
589
+ @Module({ imports: [TikTokModule.forRoot()] }) // lee TIKTOK_ACCESS_TOKEN
590
+ export class AppModule {}
591
+ ```
592
+
593
+ ```ts
594
+ await tiktok.userInfo(); // comprueba el token sin publicar
595
+ const id = await tiktok.publish({ data, title }); // sin privacyLevel: a borradores
596
+ await tiktok.status(id); // hasta PUBLISH_COMPLETE
597
+ ```
598
+
599
+ **Cuidado con `creatorInfo()`.** Parece el paso previo obligatorio —así lo
600
+ sugiere la documentación— pero solo lo es para publicar **en directo**, y
601
+ además exige `video.publish`. Llamarlo con un token de borradores devuelve
602
+ `scope_not_authorized`, o sea que falla justo en la configuración más común.
603
+ Para comprobar que el token vive, `userInfo()`, que se conforma con
604
+ `user.info.basic`.
605
+
606
+ **La diferencia entre los dos modos importa mucho al principio.** En *Direct
607
+ Post* el video sale en vivo, pero exige el permiso `video.publish`, que solo se
608
+ consigue pasando una auditoría de TikTok de varias semanas —y hasta aprobarla
609
+ todo lo publicado queda en `SELF_ONLY`, o sea que no lo ve nadie—. En modo
610
+ **borrador**, que es el de por defecto aquí, el video cae en la bandeja de la
611
+ cuenta y una persona le da publicar desde el teléfono: llega con todo su
612
+ alcance y solo necesita `video.upload`.
613
+
614
+ Dicho de otro modo: publicar en directo antes de la auditoría es peor que no
615
+ publicar, porque gastas la pieza sin que la vea nadie.
616
+
617
+ Sube el binario en vez de pasar una URL a propósito. La otra vía,
618
+ `PULL_FROM_URL`, obliga a verificar el dominio en el portal de TikTok, y
619
+ cambiar un trámite por otro no compensa cuando el archivo ya está en disco.
620
+
621
+ El video tiene que ir en **MP4 H.264**. Límite de **6 peticiones por minuto**
622
+ por token, así que el sondeo del estado va espaciado.
623
+
581
624
  ### `stripe` — cliente de la SDK oficial
582
625
 
583
626
  ```ts
@@ -1023,6 +1066,7 @@ trae los datos.
1023
1066
  | `@miguelmorales13/nestkit/meta` | `MetaModule`, `MetaService` — publicar en Instagram y Facebook Pages |
1024
1067
  | `@miguelmorales13/nestkit/media` | `MediaModule`, `MediaService` — hosting efímero en memoria |
1025
1068
  | `@miguelmorales13/nestkit/pinterest` | `PinterestModule`, `PinterestService` — publicar pines |
1069
+ | `@miguelmorales13/nestkit/tiktok` | `TikTokModule`, `TikTokService` — publicar videos |
1026
1070
  | `@miguelmorales13/nestkit/stripe` | `StripeModule`, `STRIPE_CLIENT`, `createStripeWebhookController` |
1027
1071
  | `@miguelmorales13/nestkit/auth` | `AuthUserPort`, `BaseAuthService`, `createAuthController`, `TokenService`, `JwtAuthGuard`, `RolesGuard`, `RequireTenantGuard`, `CurrentUser`, `CurrentTenant`, `Roles` |
1028
1072
  | `@miguelmorales13/nestkit/auth/oauth` | `OAuthAuthModule`, `OAuthAuthService`, `createOAuthController`, `googleProvider`, `facebookProvider`, `OAuthStorePort`, `RefreshTokenStorePort` |
@@ -1033,9 +1077,13 @@ trae los datos.
1033
1077
  | `@miguelmorales13/nestkit/i18n` | `I18nModule` (wrapper de `nestjs-i18n`), `translateOr` |
1034
1078
  | `@miguelmorales13/nestkit/bootstrap` | `applyNestKitDefaults` |
1035
1079
 
1080
+ ## Cambios recientes
1081
+
1082
+ Qué se añadió en cada versión y por qué está hecho así: [CAMBIOS.md](./CAMBIOS.md).
1083
+
1036
1084
  ## Estado del paquete
1037
1085
 
1038
- `0.8.0`. Sin adaptador Mongo real. Sin tests unitarios propios.
1086
+ `0.10.0`. Sin adaptador Mongo real. Sin tests unitarios propios.
1039
1087
 
1040
1088
  `auth/oauth` y `umami` sí tienen un consumidor real: **nutrimx** corre su acceso con Google y
1041
1089
  Facebook sobre este módulo en producción. El resto sigue sin ejercitarse de verdad.
package/dist/index.cjs CHANGED
@@ -28,6 +28,10 @@ var _chunk3CLYZC3Tcjs = require('./chunk-3CLYZC3T.cjs');
28
28
 
29
29
 
30
30
  var _chunkQS2W5XCQcjs = require('./chunk-QS2W5XCQ.cjs');
31
+
32
+
33
+
34
+ var _chunkM3EL5O6Tcjs = require('./chunk-M3EL5O6T.cjs');
31
35
  require('./chunk-7SOM7EZP.cjs');
32
36
 
33
37
 
@@ -45,10 +49,6 @@ var _chunkZA56XBCKcjs = require('./chunk-ZA56XBCK.cjs');
45
49
 
46
50
 
47
51
  var _chunkR7BVS6CIcjs = require('./chunk-R7BVS6CI.cjs');
48
-
49
-
50
-
51
- var _chunkM3EL5O6Tcjs = require('./chunk-M3EL5O6T.cjs');
52
52
  require('./chunk-2REOCMUD.cjs');
53
53
 
54
54
 
package/dist/index.js CHANGED
@@ -28,6 +28,10 @@ import {
28
28
  import {
29
29
  BaseResponseDto
30
30
  } from "./chunk-XX2HPTRU.js";
31
+ import {
32
+ I18nModule,
33
+ translateOr
34
+ } from "./chunk-IYUUYCP5.js";
31
35
  import "./chunk-DQYAIQQ5.js";
32
36
  import {
33
37
  ConflictAppException,
@@ -45,10 +49,6 @@ import {
45
49
  import {
46
50
  AppException
47
51
  } from "./chunk-YFYHLYHN.js";
48
- import {
49
- I18nModule,
50
- translateOr
51
- } from "./chunk-IYUUYCP5.js";
52
52
  import "./chunk-4MGIQFAJ.js";
53
53
  export {
54
54
  AppException,
@@ -0,0 +1,165 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
2
+
3
+ var _chunk2REOCMUDcjs = require('../chunk-2REOCMUD.cjs');
4
+
5
+ // src/tiktok/tiktok.service.ts
6
+ var _common = require('@nestjs/common');
7
+ var API = "https://open.tiktokapis.com/v2";
8
+ var TikTokService = class {
9
+ constructor(credentials) {
10
+ this.credentials = credentials;
11
+ }
12
+ get configured() {
13
+ return Boolean(this.credentials.accessToken);
14
+ }
15
+ /**
16
+ * Datos de la cuenta: qué niveles de privacidad admite —varían según si es
17
+ * privada, de empresa o de un menor— y cuánto puede durar el video.
18
+ *
19
+ * **Obligatorio solo para publicar en directo.** Para el modo borrador no
20
+ * hace falta, y llamarlo con un token que solo tiene `video.upload` falla
21
+ * con `scope_not_authorized`, porque este endpoint pide `video.publish`.
22
+ * De ahí que `publish()` solo lo consulte cuando va a publicar directo.
23
+ */
24
+ async creatorInfo() {
25
+ return this.request("/post/publish/creator_info/query/", {});
26
+ }
27
+ /**
28
+ * Sube un vídeo y devuelve el `publish_id` con el que seguir su estado.
29
+ *
30
+ * Sin `privacyLevel` va a borradores; con él, se publica directo —y para eso
31
+ * hace falta que la aplicación haya pasado la auditoría.
32
+ */
33
+ async publish(video) {
34
+ const directo = Boolean(video.privacyLevel);
35
+ const ruta = directo ? "/post/publish/video/init/" : "/post/publish/inbox/video/init/";
36
+ const cuerpo = {
37
+ source_info: {
38
+ source: "FILE_UPLOAD",
39
+ video_size: video.data.byteLength,
40
+ // Una sola parte: estos clips son de unos pocos megas y trocearlos
41
+ // solo añadiría puntos donde fallar.
42
+ chunk_size: video.data.byteLength,
43
+ total_chunk_count: 1
44
+ }
45
+ };
46
+ if (directo) {
47
+ cuerpo.post_info = {
48
+ title: video.title.slice(0, 2200),
49
+ privacy_level: video.privacyLevel
50
+ };
51
+ }
52
+ const inicio = await this.request(
53
+ ruta,
54
+ cuerpo
55
+ );
56
+ const subida = await fetch(inicio.upload_url, {
57
+ method: "PUT",
58
+ headers: {
59
+ "Content-Type": "video/mp4",
60
+ // TikTok exige el rango incluso subiendo el archivo entero de una vez.
61
+ "Content-Range": `bytes 0-${video.data.byteLength - 1}/${video.data.byteLength}`
62
+ },
63
+ body: new Uint8Array(video.data)
64
+ });
65
+ if (!subida.ok) {
66
+ throw new Error(
67
+ `TikTok rechaz\xF3 el archivo (HTTP ${subida.status}): ${(await subida.text()).slice(0, 200)}`
68
+ );
69
+ }
70
+ return inicio.publish_id;
71
+ }
72
+ /**
73
+ * Quién es el dueño del token.
74
+ *
75
+ * Sirve para comprobar que el token vive sin publicar nada: `creatorInfo`
76
+ * no vale para eso porque pide un permiso que un token de borradores no
77
+ * tiene. Solo necesita `user.info.basic`.
78
+ */
79
+ async userInfo() {
80
+ const datos = await this.get(
81
+ "/user/info/?fields=open_id,display_name"
82
+ );
83
+ return datos.user;
84
+ }
85
+ /** En qué va el procesado. Termina en `PUBLISH_COMPLETE` o en un fallo. */
86
+ async status(publishId) {
87
+ return this.request(
88
+ "/post/publish/status/fetch/",
89
+ { publish_id: publishId }
90
+ );
91
+ }
92
+ get(path) {
93
+ return this.llamar(path, "GET");
94
+ }
95
+ request(path, body) {
96
+ return this.llamar(path, "POST", body);
97
+ }
98
+ async llamar(path, method, body) {
99
+ if (!this.credentials.accessToken) {
100
+ throw new Error("TikTokService: no access token configured");
101
+ }
102
+ const response = await fetch(`${API}${path}`, {
103
+ method,
104
+ headers: {
105
+ Authorization: `Bearer ${this.credentials.accessToken}`,
106
+ "Content-Type": "application/json; charset=UTF-8"
107
+ },
108
+ ...method === "POST" ? { body: JSON.stringify(body) } : {}
109
+ });
110
+ const text = await response.text();
111
+ let json;
112
+ try {
113
+ json = JSON.parse(text);
114
+ } catch (e) {
115
+ throw new Error(`TikTok respondi\xF3 ${response.status} y no era JSON: ${text.slice(0, 200)}`);
116
+ }
117
+ if (json.error && json.error.code && json.error.code !== "ok") {
118
+ throw new Error(`TikTok: ${json.error.code} \u2014 ${_nullishCoalesce(json.error.message, () => ( ""))}`.trim());
119
+ }
120
+ if (!response.ok) {
121
+ throw new Error(`TikTok respondi\xF3 HTTP ${response.status}: ${text.slice(0, 200)}`);
122
+ }
123
+ if (!json.data) {
124
+ throw new Error(`TikTok devolvi\xF3 una respuesta sin datos: ${text.slice(0, 200)}`);
125
+ }
126
+ return json.data;
127
+ }
128
+ };
129
+ TikTokService = exports.TikTokService = _chunk2REOCMUDcjs.__decorateClass.call(void 0, [
130
+ _common.Injectable.call(void 0, )
131
+ ], TikTokService);
132
+
133
+ // src/tiktok/tiktok.module.ts
134
+
135
+ var TikTokModule = class {
136
+ static forRoot(credentials) {
137
+ return {
138
+ module: TikTokModule,
139
+ providers: [
140
+ {
141
+ provide: TikTokService,
142
+ useFactory: () => {
143
+ const resolved = _nullishCoalesce(credentials, () => ( {
144
+ accessToken: _nullishCoalesce(process.env.TIKTOK_ACCESS_TOKEN, () => ( ""))
145
+ }));
146
+ if (!resolved.accessToken) {
147
+ throw new Error(
148
+ "TikTokModule: no access token. Set TIKTOK_ACCESS_TOKEN or pass credentials to forRoot()."
149
+ );
150
+ }
151
+ return new TikTokService(resolved);
152
+ }
153
+ }
154
+ ],
155
+ exports: [TikTokService]
156
+ };
157
+ }
158
+ };
159
+ TikTokModule = exports.TikTokModule = _chunk2REOCMUDcjs.__decorateClass.call(void 0, [
160
+ _common.Module.call(void 0, {})
161
+ ], TikTokModule);
162
+
163
+
164
+
165
+ exports.TikTokModule = TikTokModule; exports.TikTokService = TikTokService;
@@ -0,0 +1,3 @@
1
+ export { TikTokService } from './tiktok.service.js';
2
+ export type { TikTokCredentials, TikTokCreatorInfo, TikTokVideo, } from './tiktok.service.js';
3
+ export { TikTokModule } from './tiktok.module.js';
@@ -0,0 +1,165 @@
1
+ import {
2
+ __decorateClass
3
+ } from "../chunk-4MGIQFAJ.js";
4
+
5
+ // src/tiktok/tiktok.service.ts
6
+ import { Injectable } from "@nestjs/common";
7
+ var API = "https://open.tiktokapis.com/v2";
8
+ var TikTokService = class {
9
+ constructor(credentials) {
10
+ this.credentials = credentials;
11
+ }
12
+ get configured() {
13
+ return Boolean(this.credentials.accessToken);
14
+ }
15
+ /**
16
+ * Datos de la cuenta: qué niveles de privacidad admite —varían según si es
17
+ * privada, de empresa o de un menor— y cuánto puede durar el video.
18
+ *
19
+ * **Obligatorio solo para publicar en directo.** Para el modo borrador no
20
+ * hace falta, y llamarlo con un token que solo tiene `video.upload` falla
21
+ * con `scope_not_authorized`, porque este endpoint pide `video.publish`.
22
+ * De ahí que `publish()` solo lo consulte cuando va a publicar directo.
23
+ */
24
+ async creatorInfo() {
25
+ return this.request("/post/publish/creator_info/query/", {});
26
+ }
27
+ /**
28
+ * Sube un vídeo y devuelve el `publish_id` con el que seguir su estado.
29
+ *
30
+ * Sin `privacyLevel` va a borradores; con él, se publica directo —y para eso
31
+ * hace falta que la aplicación haya pasado la auditoría.
32
+ */
33
+ async publish(video) {
34
+ const directo = Boolean(video.privacyLevel);
35
+ const ruta = directo ? "/post/publish/video/init/" : "/post/publish/inbox/video/init/";
36
+ const cuerpo = {
37
+ source_info: {
38
+ source: "FILE_UPLOAD",
39
+ video_size: video.data.byteLength,
40
+ // Una sola parte: estos clips son de unos pocos megas y trocearlos
41
+ // solo añadiría puntos donde fallar.
42
+ chunk_size: video.data.byteLength,
43
+ total_chunk_count: 1
44
+ }
45
+ };
46
+ if (directo) {
47
+ cuerpo.post_info = {
48
+ title: video.title.slice(0, 2200),
49
+ privacy_level: video.privacyLevel
50
+ };
51
+ }
52
+ const inicio = await this.request(
53
+ ruta,
54
+ cuerpo
55
+ );
56
+ const subida = await fetch(inicio.upload_url, {
57
+ method: "PUT",
58
+ headers: {
59
+ "Content-Type": "video/mp4",
60
+ // TikTok exige el rango incluso subiendo el archivo entero de una vez.
61
+ "Content-Range": `bytes 0-${video.data.byteLength - 1}/${video.data.byteLength}`
62
+ },
63
+ body: new Uint8Array(video.data)
64
+ });
65
+ if (!subida.ok) {
66
+ throw new Error(
67
+ `TikTok rechaz\xF3 el archivo (HTTP ${subida.status}): ${(await subida.text()).slice(0, 200)}`
68
+ );
69
+ }
70
+ return inicio.publish_id;
71
+ }
72
+ /**
73
+ * Quién es el dueño del token.
74
+ *
75
+ * Sirve para comprobar que el token vive sin publicar nada: `creatorInfo`
76
+ * no vale para eso porque pide un permiso que un token de borradores no
77
+ * tiene. Solo necesita `user.info.basic`.
78
+ */
79
+ async userInfo() {
80
+ const datos = await this.get(
81
+ "/user/info/?fields=open_id,display_name"
82
+ );
83
+ return datos.user;
84
+ }
85
+ /** En qué va el procesado. Termina en `PUBLISH_COMPLETE` o en un fallo. */
86
+ async status(publishId) {
87
+ return this.request(
88
+ "/post/publish/status/fetch/",
89
+ { publish_id: publishId }
90
+ );
91
+ }
92
+ get(path) {
93
+ return this.llamar(path, "GET");
94
+ }
95
+ request(path, body) {
96
+ return this.llamar(path, "POST", body);
97
+ }
98
+ async llamar(path, method, body) {
99
+ if (!this.credentials.accessToken) {
100
+ throw new Error("TikTokService: no access token configured");
101
+ }
102
+ const response = await fetch(`${API}${path}`, {
103
+ method,
104
+ headers: {
105
+ Authorization: `Bearer ${this.credentials.accessToken}`,
106
+ "Content-Type": "application/json; charset=UTF-8"
107
+ },
108
+ ...method === "POST" ? { body: JSON.stringify(body) } : {}
109
+ });
110
+ const text = await response.text();
111
+ let json;
112
+ try {
113
+ json = JSON.parse(text);
114
+ } catch {
115
+ throw new Error(`TikTok respondi\xF3 ${response.status} y no era JSON: ${text.slice(0, 200)}`);
116
+ }
117
+ if (json.error && json.error.code && json.error.code !== "ok") {
118
+ throw new Error(`TikTok: ${json.error.code} \u2014 ${json.error.message ?? ""}`.trim());
119
+ }
120
+ if (!response.ok) {
121
+ throw new Error(`TikTok respondi\xF3 HTTP ${response.status}: ${text.slice(0, 200)}`);
122
+ }
123
+ if (!json.data) {
124
+ throw new Error(`TikTok devolvi\xF3 una respuesta sin datos: ${text.slice(0, 200)}`);
125
+ }
126
+ return json.data;
127
+ }
128
+ };
129
+ TikTokService = __decorateClass([
130
+ Injectable()
131
+ ], TikTokService);
132
+
133
+ // src/tiktok/tiktok.module.ts
134
+ import { Module } from "@nestjs/common";
135
+ var TikTokModule = class {
136
+ static forRoot(credentials) {
137
+ return {
138
+ module: TikTokModule,
139
+ providers: [
140
+ {
141
+ provide: TikTokService,
142
+ useFactory: () => {
143
+ const resolved = credentials ?? {
144
+ accessToken: process.env.TIKTOK_ACCESS_TOKEN ?? ""
145
+ };
146
+ if (!resolved.accessToken) {
147
+ throw new Error(
148
+ "TikTokModule: no access token. Set TIKTOK_ACCESS_TOKEN or pass credentials to forRoot()."
149
+ );
150
+ }
151
+ return new TikTokService(resolved);
152
+ }
153
+ }
154
+ ],
155
+ exports: [TikTokService]
156
+ };
157
+ }
158
+ };
159
+ TikTokModule = __decorateClass([
160
+ Module({})
161
+ ], TikTokModule);
162
+ export {
163
+ TikTokModule,
164
+ TikTokService
165
+ };
@@ -0,0 +1,13 @@
1
+ import { type DynamicModule } from '@nestjs/common';
2
+ import { type TikTokCredentials } from './tiktok.service.js';
3
+ /**
4
+ * Outbound client for TikTok's Content Posting API. See `TikTokService`.
5
+ *
6
+ * Reads `TIKTOK_ACCESS_TOKEN` by default. Like `PinterestService`, the service
7
+ * takes plain credentials and uses nothing but `fetch`, so a script outside
8
+ * Nest can do `new TikTokService({ accessToken })` — which is what the caller
9
+ * holding the rendered video usually is.
10
+ */
11
+ export declare class TikTokModule {
12
+ static forRoot(credentials?: TikTokCredentials): DynamicModule;
13
+ }
@@ -0,0 +1,92 @@
1
+ export interface TikTokCredentials {
2
+ /** Token de usuario con `video.upload` o `video.publish`. */
3
+ accessToken: string;
4
+ }
5
+ /** Lo que TikTok cuenta de la cuenta antes de dejar publicar. */
6
+ export interface TikTokCreatorInfo {
7
+ creator_username: string;
8
+ creator_nickname: string;
9
+ /** Niveles de privacidad que la cuenta admite. Ver `publish`. */
10
+ privacy_level_options: string[];
11
+ max_video_post_duration_sec: number;
12
+ }
13
+ export interface TikTokVideo {
14
+ /** El vídeo, ya en MP4 H.264. */
15
+ data: Buffer;
16
+ /** El texto del post. Hasta 2200 caracteres. */
17
+ title: string;
18
+ /**
19
+ * Cómo queda publicado. Si se omite, va a **borradores**: aparece en la
20
+ * bandeja de TikTok de la cuenta y una persona le da publicar.
21
+ *
22
+ * Ese es el modo que conviene mientras la aplicación no haya pasado la
23
+ * auditoría de TikTok, porque el modo directo publica en `SELF_ONLY` hasta
24
+ * entonces —es decir, no lo ve nadie— mientras que el borrador llega igual
25
+ * y se publica a mano sin perder alcance.
26
+ */
27
+ privacyLevel?: string;
28
+ }
29
+ /**
30
+ * Publica vídeos con la Content Posting API de TikTok.
31
+ *
32
+ * Publicar son dos llamadas: inicializar la subida y sondear hasta que TikTok
33
+ * termine de procesar. Hay una tercera, `creatorInfo`, que es obligatoria
34
+ * **solo** para publicar en directo —y que además exige `video.publish`, así
35
+ * que llamarla con un token de borradores falla.
36
+ *
37
+ * **Dos modos, y la diferencia importa mucho al principio.** En *Direct Post*
38
+ * el vídeo sale en vivo, pero exige el permiso `video.publish`, que solo se
39
+ * consigue pasando una auditoría de TikTok de varias semanas; hasta aprobarla
40
+ * todo lo publicado queda en `SELF_ONLY` y no lo ve nadie. En modo *borrador*
41
+ * —el de por defecto aquí— el vídeo cae en la bandeja de la cuenta y una
42
+ * persona le da publicar desde el teléfono: llega con todo su alcance y solo
43
+ * necesita `video.upload`.
44
+ *
45
+ * Se sube el binario en vez de pasar una URL a propósito. La otra vía,
46
+ * `PULL_FROM_URL`, obliga a verificar el dominio en el portal de TikTok, y
47
+ * cambiar un trámite por otro no compensa cuando el archivo ya está en disco.
48
+ *
49
+ * El límite es de 6 peticiones por minuto y por token, así que el sondeo del
50
+ * estado va espaciado.
51
+ */
52
+ export declare class TikTokService {
53
+ private readonly credentials;
54
+ constructor(credentials: TikTokCredentials);
55
+ get configured(): boolean;
56
+ /**
57
+ * Datos de la cuenta: qué niveles de privacidad admite —varían según si es
58
+ * privada, de empresa o de un menor— y cuánto puede durar el video.
59
+ *
60
+ * **Obligatorio solo para publicar en directo.** Para el modo borrador no
61
+ * hace falta, y llamarlo con un token que solo tiene `video.upload` falla
62
+ * con `scope_not_authorized`, porque este endpoint pide `video.publish`.
63
+ * De ahí que `publish()` solo lo consulte cuando va a publicar directo.
64
+ */
65
+ creatorInfo(): Promise<TikTokCreatorInfo>;
66
+ /**
67
+ * Sube un vídeo y devuelve el `publish_id` con el que seguir su estado.
68
+ *
69
+ * Sin `privacyLevel` va a borradores; con él, se publica directo —y para eso
70
+ * hace falta que la aplicación haya pasado la auditoría.
71
+ */
72
+ publish(video: TikTokVideo): Promise<string>;
73
+ /**
74
+ * Quién es el dueño del token.
75
+ *
76
+ * Sirve para comprobar que el token vive sin publicar nada: `creatorInfo`
77
+ * no vale para eso porque pide un permiso que un token de borradores no
78
+ * tiene. Solo necesita `user.info.basic`.
79
+ */
80
+ userInfo(): Promise<{
81
+ open_id: string;
82
+ display_name: string;
83
+ }>;
84
+ /** En qué va el procesado. Termina en `PUBLISH_COMPLETE` o en un fallo. */
85
+ status(publishId: string): Promise<{
86
+ status: string;
87
+ fail_reason?: string;
88
+ }>;
89
+ private get;
90
+ private request;
91
+ private llamar;
92
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miguelmorales13/nestkit",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -121,6 +121,11 @@
121
121
  "import": "./dist/pinterest/index.js",
122
122
  "require": "./dist/pinterest/index.cjs"
123
123
  },
124
+ "./tiktok": {
125
+ "types": "./dist/tiktok/index.d.ts",
126
+ "import": "./dist/tiktok/index.js",
127
+ "require": "./dist/tiktok/index.cjs"
128
+ },
124
129
  "./media": {
125
130
  "types": "./dist/media/index.d.ts",
126
131
  "import": "./dist/media/index.js",