@miguelmorales13/nestkit 0.3.0 → 0.4.1

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.
@@ -1,11 +1,11 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkO2ZMI3EKcjs = require('../chunk-O2ZMI3EK.cjs');
4
- require('../chunk-7FQQDWOZ.cjs');
5
- require('../chunk-R7BVS6CI.cjs');
3
+ var _chunk54ZXIB5Tcjs = require('../chunk-54ZXIB5T.cjs');
6
4
  require('../chunk-TJHRABML.cjs');
5
+ require('../chunk-J7TURDAL.cjs');
7
6
  require('../chunk-ZA56XBCK.cjs');
7
+ require('../chunk-R7BVS6CI.cjs');
8
8
  require('../chunk-2REOCMUD.cjs');
9
9
 
10
10
 
11
- exports.applyNestKitDefaults = _chunkO2ZMI3EKcjs.applyNestKitDefaults;
11
+ exports.applyNestKitDefaults = _chunk54ZXIB5Tcjs.applyNestKitDefaults;
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  applyNestKitDefaults
3
- } from "../chunk-RHNQGTAT.js";
4
- import "../chunk-Q45IEPWU.js";
5
- import "../chunk-YFYHLYHN.js";
3
+ } from "../chunk-AOCF5QCZ.js";
6
4
  import "../chunk-KDAA6GFF.js";
5
+ import "../chunk-7I2Y7V52.js";
7
6
  import "../chunk-EBO6UKHL.js";
7
+ import "../chunk-YFYHLYHN.js";
8
8
  import "../chunk-4MGIQFAJ.js";
9
9
  export {
10
10
  applyNestKitDefaults
@@ -1,9 +1,9 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
- var _chunk7FQQDWOZcjs = require('./chunk-7FQQDWOZ.cjs');
3
+ var _chunkTJHRABMLcjs = require('./chunk-TJHRABML.cjs');
4
4
 
5
5
 
6
- var _chunkTJHRABMLcjs = require('./chunk-TJHRABML.cjs');
6
+ var _chunkJ7TURDALcjs = require('./chunk-J7TURDAL.cjs');
7
7
 
8
8
  // src/bootstrap/apply-defaults.ts
9
9
 
@@ -17,7 +17,7 @@ function applyNestKitDefaults(app, options = {}) {
17
17
  app.useGlobalPipes(
18
18
  new (0, _common.ValidationPipe)({ whitelist: true, transform: true, ...options.validation })
19
19
  );
20
- app.useGlobalFilters(new (0, _chunk7FQQDWOZcjs.GlobalExceptionFilter)());
20
+ app.useGlobalFilters(new (0, _chunkJ7TURDALcjs.GlobalExceptionFilter)());
21
21
  app.useGlobalInterceptors(new (0, _chunkTJHRABMLcjs.ResponseInterceptor)());
22
22
  if (options.swagger !== false) {
23
23
  const swaggerOptions = _nullishCoalesce(options.swagger, () => ( { title: "API" }));
@@ -1,9 +1,9 @@
1
- import {
2
- GlobalExceptionFilter
3
- } from "./chunk-Q45IEPWU.js";
4
1
  import {
5
2
  ResponseInterceptor
6
3
  } from "./chunk-KDAA6GFF.js";
4
+ import {
5
+ GlobalExceptionFilter
6
+ } from "./chunk-7I2Y7V52.js";
7
7
 
8
8
  // src/bootstrap/apply-defaults.ts
9
9
  import {
@@ -8,11 +8,11 @@
8
8
  var _chunkFDNGAYTZcjs = require('../chunk-FDNGAYTZ.cjs');
9
9
 
10
10
 
11
- var _chunk7FQQDWOZcjs = require('../chunk-7FQQDWOZ.cjs');
11
+ var _chunkJ7TURDALcjs = require('../chunk-J7TURDAL.cjs');
12
+ require('../chunk-ZA56XBCK.cjs');
12
13
 
13
14
 
14
15
  var _chunkR7BVS6CIcjs = require('../chunk-R7BVS6CI.cjs');
15
- require('../chunk-ZA56XBCK.cjs');
16
16
  require('../chunk-2REOCMUD.cjs');
17
17
 
18
18
 
@@ -22,4 +22,4 @@ require('../chunk-2REOCMUD.cjs');
22
22
 
23
23
 
24
24
 
25
- exports.AppException = _chunkR7BVS6CIcjs.AppException; exports.ConflictAppException = _chunkFDNGAYTZcjs.ConflictAppException; exports.ForbiddenAppException = _chunkFDNGAYTZcjs.ForbiddenAppException; exports.GlobalExceptionFilter = _chunk7FQQDWOZcjs.GlobalExceptionFilter; exports.NotFoundAppException = _chunkFDNGAYTZcjs.NotFoundAppException; exports.UnauthorizedAppException = _chunkFDNGAYTZcjs.UnauthorizedAppException; exports.ValidationAppException = _chunkFDNGAYTZcjs.ValidationAppException;
25
+ exports.AppException = _chunkR7BVS6CIcjs.AppException; exports.ConflictAppException = _chunkFDNGAYTZcjs.ConflictAppException; exports.ForbiddenAppException = _chunkFDNGAYTZcjs.ForbiddenAppException; exports.GlobalExceptionFilter = _chunkJ7TURDALcjs.GlobalExceptionFilter; exports.NotFoundAppException = _chunkFDNGAYTZcjs.NotFoundAppException; exports.UnauthorizedAppException = _chunkFDNGAYTZcjs.UnauthorizedAppException; exports.ValidationAppException = _chunkFDNGAYTZcjs.ValidationAppException;
@@ -8,11 +8,11 @@ import {
8
8
  } from "../chunk-ORWJ7LES.js";
9
9
  import {
10
10
  GlobalExceptionFilter
11
- } from "../chunk-Q45IEPWU.js";
11
+ } from "../chunk-7I2Y7V52.js";
12
+ import "../chunk-EBO6UKHL.js";
12
13
  import {
13
14
  AppException
14
15
  } from "../chunk-YFYHLYHN.js";
15
- import "../chunk-EBO6UKHL.js";
16
16
  import "../chunk-4MGIQFAJ.js";
17
17
  export {
18
18
  AppException,
package/dist/index.cjs CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
 
4
4
 
5
+ var _chunkRF75KC63cjs = require('./chunk-RF75KC63.cjs');
6
+ require('./chunk-MR2IFCZE.cjs');
7
+
8
+
9
+
10
+
5
11
  var _chunkV2M75FN3cjs = require('./chunk-V2M75FN3.cjs');
6
12
  require('./chunk-KVBQBT3D.cjs');
7
13
 
@@ -11,16 +17,14 @@ require('./chunk-KVBQBT3D.cjs');
11
17
  var _chunkSPTDXUEDcjs = require('./chunk-SPTDXUED.cjs');
12
18
 
13
19
 
20
+ var _chunk54ZXIB5Tcjs = require('./chunk-54ZXIB5T.cjs');
14
21
 
15
22
 
16
- var _chunkRF75KC63cjs = require('./chunk-RF75KC63.cjs');
23
+ var _chunkTJHRABMLcjs = require('./chunk-TJHRABML.cjs');
17
24
 
18
25
 
19
26
 
20
27
  var _chunk3CLYZC3Tcjs = require('./chunk-3CLYZC3T.cjs');
21
-
22
-
23
- var _chunkQS2W5XCQcjs = require('./chunk-QS2W5XCQ.cjs');
24
28
  require('./chunk-7SOM7EZP.cjs');
25
29
 
26
30
 
@@ -31,10 +35,10 @@ require('./chunk-7SOM7EZP.cjs');
31
35
  var _chunkFDNGAYTZcjs = require('./chunk-FDNGAYTZ.cjs');
32
36
 
33
37
 
34
- var _chunkO2ZMI3EKcjs = require('./chunk-O2ZMI3EK.cjs');
38
+ var _chunkJ7TURDALcjs = require('./chunk-J7TURDAL.cjs');
35
39
 
36
40
 
37
- var _chunk7FQQDWOZcjs = require('./chunk-7FQQDWOZ.cjs');
41
+ var _chunkZA56XBCKcjs = require('./chunk-ZA56XBCK.cjs');
38
42
 
39
43
 
40
44
  var _chunkR7BVS6CIcjs = require('./chunk-R7BVS6CI.cjs');
@@ -42,13 +46,9 @@ var _chunkR7BVS6CIcjs = require('./chunk-R7BVS6CI.cjs');
42
46
 
43
47
 
44
48
  var _chunkM3EL5O6Tcjs = require('./chunk-M3EL5O6T.cjs');
45
- require('./chunk-MR2IFCZE.cjs');
46
49
 
47
50
 
48
- var _chunkTJHRABMLcjs = require('./chunk-TJHRABML.cjs');
49
-
50
-
51
- var _chunkZA56XBCKcjs = require('./chunk-ZA56XBCK.cjs');
51
+ var _chunkQS2W5XCQcjs = require('./chunk-QS2W5XCQ.cjs');
52
52
  require('./chunk-2REOCMUD.cjs');
53
53
 
54
54
 
@@ -75,4 +75,4 @@ require('./chunk-2REOCMUD.cjs');
75
75
 
76
76
 
77
77
 
78
- exports.AppException = _chunkR7BVS6CIcjs.AppException; exports.BaseCrudService = _chunk3CLYZC3Tcjs.BaseCrudService; exports.BaseResponseDto = _chunkQS2W5XCQcjs.BaseResponseDto; exports.ConflictAppException = _chunkFDNGAYTZcjs.ConflictAppException; exports.ForbiddenAppException = _chunkFDNGAYTZcjs.ForbiddenAppException; exports.GlobalExceptionFilter = _chunk7FQQDWOZcjs.GlobalExceptionFilter; exports.I18nModule = _chunkM3EL5O6Tcjs.I18nModule; exports.NotFoundAppException = _chunkFDNGAYTZcjs.NotFoundAppException; exports.PG_POOL = _chunkRF75KC63cjs.PG_POOL; exports.PgModule = _chunkRF75KC63cjs.PgModule; exports.REQUEST_ID_HEADER = _chunkV2M75FN3cjs.REQUEST_ID_HEADER; exports.RequestContext = _chunkZA56XBCKcjs.RequestContext; exports.RequestIdMiddleware = _chunkV2M75FN3cjs.RequestIdMiddleware; exports.ResponseInterceptor = _chunkTJHRABMLcjs.ResponseInterceptor; exports.SUPABASE_ANON_CLIENT = _chunkSPTDXUEDcjs.SUPABASE_ANON_CLIENT; exports.SUPABASE_SERVICE_ROLE_CLIENT = _chunkSPTDXUEDcjs.SUPABASE_SERVICE_ROLE_CLIENT; exports.SupabaseModule = _chunkSPTDXUEDcjs.SupabaseModule; exports.TrackingModule = _chunkV2M75FN3cjs.TrackingModule; exports.UnauthorizedAppException = _chunkFDNGAYTZcjs.UnauthorizedAppException; exports.ValidationAppException = _chunkFDNGAYTZcjs.ValidationAppException; exports.applyNestKitDefaults = _chunkO2ZMI3EKcjs.applyNestKitDefaults; exports.createCrudController = _chunk3CLYZC3Tcjs.createCrudController; exports.translateOr = _chunkM3EL5O6Tcjs.translateOr; exports.withTenantScope = _chunkRF75KC63cjs.withTenantScope;
78
+ exports.AppException = _chunkR7BVS6CIcjs.AppException; exports.BaseCrudService = _chunk3CLYZC3Tcjs.BaseCrudService; exports.BaseResponseDto = _chunkQS2W5XCQcjs.BaseResponseDto; exports.ConflictAppException = _chunkFDNGAYTZcjs.ConflictAppException; exports.ForbiddenAppException = _chunkFDNGAYTZcjs.ForbiddenAppException; exports.GlobalExceptionFilter = _chunkJ7TURDALcjs.GlobalExceptionFilter; exports.I18nModule = _chunkM3EL5O6Tcjs.I18nModule; exports.NotFoundAppException = _chunkFDNGAYTZcjs.NotFoundAppException; exports.PG_POOL = _chunkRF75KC63cjs.PG_POOL; exports.PgModule = _chunkRF75KC63cjs.PgModule; exports.REQUEST_ID_HEADER = _chunkV2M75FN3cjs.REQUEST_ID_HEADER; exports.RequestContext = _chunkZA56XBCKcjs.RequestContext; exports.RequestIdMiddleware = _chunkV2M75FN3cjs.RequestIdMiddleware; exports.ResponseInterceptor = _chunkTJHRABMLcjs.ResponseInterceptor; exports.SUPABASE_ANON_CLIENT = _chunkSPTDXUEDcjs.SUPABASE_ANON_CLIENT; exports.SUPABASE_SERVICE_ROLE_CLIENT = _chunkSPTDXUEDcjs.SUPABASE_SERVICE_ROLE_CLIENT; exports.SupabaseModule = _chunkSPTDXUEDcjs.SupabaseModule; exports.TrackingModule = _chunkV2M75FN3cjs.TrackingModule; exports.UnauthorizedAppException = _chunkFDNGAYTZcjs.UnauthorizedAppException; exports.ValidationAppException = _chunkFDNGAYTZcjs.ValidationAppException; exports.applyNestKitDefaults = _chunk54ZXIB5Tcjs.applyNestKitDefaults; exports.createCrudController = _chunk3CLYZC3Tcjs.createCrudController; exports.translateOr = _chunkM3EL5O6Tcjs.translateOr; exports.withTenantScope = _chunkRF75KC63cjs.withTenantScope;
package/dist/index.js CHANGED
@@ -1,3 +1,9 @@
1
+ import {
2
+ PG_POOL,
3
+ PgModule,
4
+ withTenantScope
5
+ } from "./chunk-VKOPDDCC.js";
6
+ import "./chunk-NAK4WDKS.js";
1
7
  import {
2
8
  REQUEST_ID_HEADER,
3
9
  RequestIdMiddleware,
@@ -10,17 +16,15 @@ import {
10
16
  SupabaseModule
11
17
  } from "./chunk-PA24P76K.js";
12
18
  import {
13
- PG_POOL,
14
- PgModule,
15
- withTenantScope
16
- } from "./chunk-VKOPDDCC.js";
19
+ applyNestKitDefaults
20
+ } from "./chunk-AOCF5QCZ.js";
21
+ import {
22
+ ResponseInterceptor
23
+ } from "./chunk-KDAA6GFF.js";
17
24
  import {
18
25
  BaseCrudService,
19
26
  createCrudController
20
27
  } from "./chunk-JOVBJDJ2.js";
21
- import {
22
- BaseResponseDto
23
- } from "./chunk-XX2HPTRU.js";
24
28
  import "./chunk-DQYAIQQ5.js";
25
29
  import {
26
30
  ConflictAppException,
@@ -29,12 +33,12 @@ import {
29
33
  UnauthorizedAppException,
30
34
  ValidationAppException
31
35
  } from "./chunk-ORWJ7LES.js";
32
- import {
33
- applyNestKitDefaults
34
- } from "./chunk-RHNQGTAT.js";
35
36
  import {
36
37
  GlobalExceptionFilter
37
- } from "./chunk-Q45IEPWU.js";
38
+ } from "./chunk-7I2Y7V52.js";
39
+ import {
40
+ RequestContext
41
+ } from "./chunk-EBO6UKHL.js";
38
42
  import {
39
43
  AppException
40
44
  } from "./chunk-YFYHLYHN.js";
@@ -42,13 +46,9 @@ import {
42
46
  I18nModule,
43
47
  translateOr
44
48
  } from "./chunk-IYUUYCP5.js";
45
- import "./chunk-NAK4WDKS.js";
46
49
  import {
47
- ResponseInterceptor
48
- } from "./chunk-KDAA6GFF.js";
49
- import {
50
- RequestContext
51
- } from "./chunk-EBO6UKHL.js";
50
+ BaseResponseDto
51
+ } from "./chunk-XX2HPTRU.js";
52
52
  import "./chunk-4MGIQFAJ.js";
53
53
  export {
54
54
  AppException,
@@ -0,0 +1,135 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+
4
+ var _chunk2REOCMUDcjs = require('../chunk-2REOCMUD.cjs');
5
+
6
+ // src/media/media.module.ts
7
+ var _common = require('@nestjs/common');
8
+
9
+ // src/media/media.controller.ts
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+ // src/media/media.service.ts
24
+
25
+
26
+ // src/media/media.options.ts
27
+ var MEDIA_OPTIONS = /* @__PURE__ */ Symbol("MEDIA_OPTIONS");
28
+ var MEDIA_TTL = /* @__PURE__ */ Symbol("MEDIA_TTL");
29
+
30
+ // src/media/media.service.ts
31
+ var _crypto = require('crypto');
32
+ var MediaService = class {
33
+ constructor(ttlMs) {
34
+ this.ttlMs = ttlMs;
35
+ this.logger = new (0, _common.Logger)(MediaService.name);
36
+ this.store = /* @__PURE__ */ new Map();
37
+ }
38
+ put(data, contentType) {
39
+ this.sweep();
40
+ const id = _crypto.randomUUID.call(void 0, ).replace(/-/g, "");
41
+ this.store.set(id, { data, contentType, expiresAt: Date.now() + this.ttlMs });
42
+ this.logger.log(`Stored ${id} (${(data.length / 1024).toFixed(0)} KB)`);
43
+ return id;
44
+ }
45
+ get(id) {
46
+ const found = this.store.get(id);
47
+ if (!found) return void 0;
48
+ if (Date.now() > found.expiresAt) {
49
+ this.store.delete(id);
50
+ return void 0;
51
+ }
52
+ return found;
53
+ }
54
+ /** Swept on write, which is the only moment the map can have grown. */
55
+ sweep() {
56
+ const now = Date.now();
57
+ for (const [id, entry] of this.store) {
58
+ if (now > entry.expiresAt) this.store.delete(id);
59
+ }
60
+ }
61
+ };
62
+ MediaService = exports.MediaService = _chunk2REOCMUDcjs.__decorateClass.call(void 0, [
63
+ _common.Injectable.call(void 0, ),
64
+ _chunk2REOCMUDcjs.__decorateParam.call(void 0, 0, _common.Inject.call(void 0, MEDIA_TTL))
65
+ ], MediaService);
66
+
67
+ // src/media/media.controller.ts
68
+ var MediaController = class {
69
+ // Both dependencies are injected by explicit token, like the rest of this
70
+ // package. The bundler does not emit decorator metadata, so injecting by type
71
+ // alone leaves Nest unable to resolve the constructor at runtime — and the
72
+ // failure only shows when the consuming app boots, never at build time.
73
+ constructor(media, options) {
74
+ this.media = media;
75
+ this.options = options;
76
+ }
77
+ upload(request, secret) {
78
+ if (!this.options.uploadSecret) {
79
+ throw new (0, _common.UnauthorizedException)("Media uploads are not configured.");
80
+ }
81
+ if (secret !== this.options.uploadSecret) {
82
+ throw new (0, _common.UnauthorizedException)("Invalid secret.");
83
+ }
84
+ const data = request.body;
85
+ if (!_optionalChain([data, 'optionalAccess', _ => _.length])) throw new (0, _common.UnauthorizedException)("Empty body.");
86
+ const id = this.media.put(Buffer.from(data), _nullishCoalesce(request.headers["content-type"], () => ( "image/png")));
87
+ return { url: `${this.options.publicUrl}/media/${id}` };
88
+ }
89
+ serve(id, reply) {
90
+ const found = this.media.get(id);
91
+ if (!found) throw new (0, _common.NotFoundException)("That file is no longer available.");
92
+ reply.header("content-type", found.contentType).header("cache-control", "no-store").send(found.data);
93
+ }
94
+ };
95
+ _chunk2REOCMUDcjs.__decorateClass.call(void 0, [
96
+ _common.Post.call(void 0, "media"),
97
+ _chunk2REOCMUDcjs.__decorateParam.call(void 0, 0, _common.Req.call(void 0, )),
98
+ _chunk2REOCMUDcjs.__decorateParam.call(void 0, 1, _common.Headers.call(void 0, "x-media-secret"))
99
+ ], MediaController.prototype, "upload", 1);
100
+ _chunk2REOCMUDcjs.__decorateClass.call(void 0, [
101
+ _common.Get.call(void 0, "media/:id"),
102
+ _chunk2REOCMUDcjs.__decorateParam.call(void 0, 0, _common.Param.call(void 0, "id")),
103
+ _chunk2REOCMUDcjs.__decorateParam.call(void 0, 1, _common.Res.call(void 0, ))
104
+ ], MediaController.prototype, "serve", 1);
105
+ MediaController = _chunk2REOCMUDcjs.__decorateClass.call(void 0, [
106
+ _common.Controller.call(void 0, ),
107
+ _chunk2REOCMUDcjs.__decorateParam.call(void 0, 0, _common.Inject.call(void 0, MediaService)),
108
+ _chunk2REOCMUDcjs.__decorateParam.call(void 0, 1, _common.Inject.call(void 0, MEDIA_OPTIONS))
109
+ ], MediaController);
110
+
111
+ // src/media/media.module.ts
112
+ var ONE_HOUR = 60 * 60 * 1e3;
113
+ var MediaModule = class {
114
+ static forRoot(options) {
115
+ return {
116
+ module: MediaModule,
117
+ controllers: [MediaController],
118
+ providers: [
119
+ { provide: MEDIA_OPTIONS, useValue: options },
120
+ { provide: MEDIA_TTL, useValue: _nullishCoalesce(options.ttlMs, () => ( ONE_HOUR)) },
121
+ MediaService
122
+ ],
123
+ exports: [MediaService]
124
+ };
125
+ }
126
+ };
127
+ MediaModule = exports.MediaModule = _chunk2REOCMUDcjs.__decorateClass.call(void 0, [
128
+ _common.Module.call(void 0, {})
129
+ ], MediaModule);
130
+
131
+
132
+
133
+
134
+
135
+ exports.MEDIA_OPTIONS = MEDIA_OPTIONS; exports.MEDIA_TTL = MEDIA_TTL; exports.MediaModule = MediaModule; exports.MediaService = MediaService;
@@ -0,0 +1,5 @@
1
+ export { MediaModule } from './media.module.js';
2
+ export { MediaService } from './media.service.js';
3
+ export { MEDIA_OPTIONS, MEDIA_TTL } from './media.options.js';
4
+ export type { MediaOptions } from './media.options.js';
5
+ export type { StoredMedia } from './media.service.js';
@@ -0,0 +1,135 @@
1
+ import {
2
+ __decorateClass,
3
+ __decorateParam
4
+ } from "../chunk-4MGIQFAJ.js";
5
+
6
+ // src/media/media.module.ts
7
+ import { Module } from "@nestjs/common";
8
+
9
+ // src/media/media.controller.ts
10
+ import {
11
+ Controller,
12
+ Get,
13
+ Headers,
14
+ Inject as Inject2,
15
+ NotFoundException,
16
+ Param,
17
+ Post,
18
+ Req,
19
+ Res,
20
+ UnauthorizedException
21
+ } from "@nestjs/common";
22
+
23
+ // src/media/media.service.ts
24
+ import { Inject, Injectable, Logger } from "@nestjs/common";
25
+
26
+ // src/media/media.options.ts
27
+ var MEDIA_OPTIONS = /* @__PURE__ */ Symbol("MEDIA_OPTIONS");
28
+ var MEDIA_TTL = /* @__PURE__ */ Symbol("MEDIA_TTL");
29
+
30
+ // src/media/media.service.ts
31
+ import { randomUUID } from "crypto";
32
+ var MediaService = class {
33
+ constructor(ttlMs) {
34
+ this.ttlMs = ttlMs;
35
+ this.logger = new Logger(MediaService.name);
36
+ this.store = /* @__PURE__ */ new Map();
37
+ }
38
+ put(data, contentType) {
39
+ this.sweep();
40
+ const id = randomUUID().replace(/-/g, "");
41
+ this.store.set(id, { data, contentType, expiresAt: Date.now() + this.ttlMs });
42
+ this.logger.log(`Stored ${id} (${(data.length / 1024).toFixed(0)} KB)`);
43
+ return id;
44
+ }
45
+ get(id) {
46
+ const found = this.store.get(id);
47
+ if (!found) return void 0;
48
+ if (Date.now() > found.expiresAt) {
49
+ this.store.delete(id);
50
+ return void 0;
51
+ }
52
+ return found;
53
+ }
54
+ /** Swept on write, which is the only moment the map can have grown. */
55
+ sweep() {
56
+ const now = Date.now();
57
+ for (const [id, entry] of this.store) {
58
+ if (now > entry.expiresAt) this.store.delete(id);
59
+ }
60
+ }
61
+ };
62
+ MediaService = __decorateClass([
63
+ Injectable(),
64
+ __decorateParam(0, Inject(MEDIA_TTL))
65
+ ], MediaService);
66
+
67
+ // src/media/media.controller.ts
68
+ var MediaController = class {
69
+ // Both dependencies are injected by explicit token, like the rest of this
70
+ // package. The bundler does not emit decorator metadata, so injecting by type
71
+ // alone leaves Nest unable to resolve the constructor at runtime — and the
72
+ // failure only shows when the consuming app boots, never at build time.
73
+ constructor(media, options) {
74
+ this.media = media;
75
+ this.options = options;
76
+ }
77
+ upload(request, secret) {
78
+ if (!this.options.uploadSecret) {
79
+ throw new UnauthorizedException("Media uploads are not configured.");
80
+ }
81
+ if (secret !== this.options.uploadSecret) {
82
+ throw new UnauthorizedException("Invalid secret.");
83
+ }
84
+ const data = request.body;
85
+ if (!data?.length) throw new UnauthorizedException("Empty body.");
86
+ const id = this.media.put(Buffer.from(data), request.headers["content-type"] ?? "image/png");
87
+ return { url: `${this.options.publicUrl}/media/${id}` };
88
+ }
89
+ serve(id, reply) {
90
+ const found = this.media.get(id);
91
+ if (!found) throw new NotFoundException("That file is no longer available.");
92
+ reply.header("content-type", found.contentType).header("cache-control", "no-store").send(found.data);
93
+ }
94
+ };
95
+ __decorateClass([
96
+ Post("media"),
97
+ __decorateParam(0, Req()),
98
+ __decorateParam(1, Headers("x-media-secret"))
99
+ ], MediaController.prototype, "upload", 1);
100
+ __decorateClass([
101
+ Get("media/:id"),
102
+ __decorateParam(0, Param("id")),
103
+ __decorateParam(1, Res())
104
+ ], MediaController.prototype, "serve", 1);
105
+ MediaController = __decorateClass([
106
+ Controller(),
107
+ __decorateParam(0, Inject2(MediaService)),
108
+ __decorateParam(1, Inject2(MEDIA_OPTIONS))
109
+ ], MediaController);
110
+
111
+ // src/media/media.module.ts
112
+ var ONE_HOUR = 60 * 60 * 1e3;
113
+ var MediaModule = class {
114
+ static forRoot(options) {
115
+ return {
116
+ module: MediaModule,
117
+ controllers: [MediaController],
118
+ providers: [
119
+ { provide: MEDIA_OPTIONS, useValue: options },
120
+ { provide: MEDIA_TTL, useValue: options.ttlMs ?? ONE_HOUR },
121
+ MediaService
122
+ ],
123
+ exports: [MediaService]
124
+ };
125
+ }
126
+ };
127
+ MediaModule = __decorateClass([
128
+ Module({})
129
+ ], MediaModule);
130
+ export {
131
+ MEDIA_OPTIONS,
132
+ MEDIA_TTL,
133
+ MediaModule,
134
+ MediaService
135
+ };
@@ -0,0 +1,44 @@
1
+ import { MediaService } from './media.service.js';
2
+ import { type MediaOptions } from './media.options.js';
3
+ /**
4
+ * The little that this controller needs from the HTTP layer, declared here so
5
+ * the package does not take a dependency on Fastify. It works with any adapter
6
+ * whose reply object exposes `header` and `send`.
7
+ */
8
+ interface RawRequest {
9
+ body?: unknown;
10
+ headers: Record<string, string | string[] | undefined>;
11
+ }
12
+ interface RawReply {
13
+ header(name: string, value: string): RawReply;
14
+ send(payload: unknown): unknown;
15
+ }
16
+ /**
17
+ * Uploads a file and serves it until it expires.
18
+ *
19
+ * The upload is guarded by a shared secret rather than a JWT because the caller
20
+ * is a CI job, not a person with a session. Without it anyone could host
21
+ * arbitrary content on your domain.
22
+ *
23
+ * The download is deliberately public: the services that consume these URLs
24
+ * fetch them without credentials.
25
+ *
26
+ * Note that Fastify only parses JSON out of the box. Register a parser for the
27
+ * content types you intend to accept, or uploads answer 415:
28
+ *
29
+ * ```ts
30
+ * app.getHttpAdapter().getInstance().addContentTypeParser(
31
+ * /^image\//, { parseAs: 'buffer' }, (_req, body, done) => done(null, body),
32
+ * )
33
+ * ```
34
+ */
35
+ export declare class MediaController {
36
+ private readonly media;
37
+ private readonly options;
38
+ constructor(media: MediaService, options: MediaOptions);
39
+ upload(request: RawRequest, secret?: string): {
40
+ url: string;
41
+ };
42
+ serve(id: string, reply: RawReply): void;
43
+ }
44
+ export {};
@@ -0,0 +1,7 @@
1
+ import { type DynamicModule } from '@nestjs/common';
2
+ import { type MediaOptions } from './media.options.js';
3
+ /** Short-lived file hosting for APIs that fetch by URL instead of accepting
4
+ * an upload. See `MediaService` for why it lives in memory. */
5
+ export declare class MediaModule {
6
+ static forRoot(options: MediaOptions): DynamicModule;
7
+ }
@@ -0,0 +1,14 @@
1
+ export declare const MEDIA_OPTIONS: unique symbol;
2
+ export declare const MEDIA_TTL: unique symbol;
3
+ export interface MediaOptions {
4
+ /**
5
+ * Base URL the generated links point to, **including the global prefix** if
6
+ * the app has one. Getting this wrong is the classic mistake: the link
7
+ * resolves to the frontend and returns an HTML page with a 200.
8
+ */
9
+ publicUrl: string;
10
+ /** Shared secret required to upload. Without it, uploads are refused. */
11
+ uploadSecret?: string;
12
+ /** How long a file stays available. Defaults to one hour. */
13
+ ttlMs?: number;
14
+ }
@@ -0,0 +1,28 @@
1
+ export interface StoredMedia {
2
+ data: Buffer;
3
+ contentType: string;
4
+ expiresAt: number;
5
+ }
6
+ /**
7
+ * Short-lived store for files that an external service has to fetch by URL.
8
+ *
9
+ * It exists because of APIs like Meta's: they refuse the binary and insist on
10
+ * downloading the file themselves, but only need it during the call — Instagram
11
+ * keeps its own copy and serves the image from its CDN afterwards. Without
12
+ * something like this the usual workaround is committing the images to the
13
+ * repository, which keeps them forever for a URL that mattered for one minute.
14
+ *
15
+ * Kept in memory on purpose. Seconds pass between storing and publishing, so a
16
+ * restart in between just means retrying, and nothing is left behind on disk.
17
+ * If you need files to survive restarts, this is the wrong tool.
18
+ */
19
+ export declare class MediaService {
20
+ private readonly ttlMs;
21
+ private readonly logger;
22
+ private readonly store;
23
+ constructor(ttlMs: number);
24
+ put(data: Buffer, contentType: string): string;
25
+ get(id: string): StoredMedia | undefined;
26
+ /** Swept on write, which is the only moment the map can have grown. */
27
+ private sweep;
28
+ }
@@ -0,0 +1,164 @@
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/meta/meta.module.ts
6
+ var _common = require('@nestjs/common');
7
+
8
+ // src/meta/meta.service.ts
9
+
10
+ var GRAPH = "https://graph.facebook.com/v21.0";
11
+ var MetaService = class {
12
+ constructor(credentials) {
13
+ this.credentials = credentials;
14
+ }
15
+ /**
16
+ * Publishes an image to Instagram. Returns the media id.
17
+ *
18
+ * Instagram publishes in two steps: a container is created with the image and
19
+ * caption, and then published. Between the two Meta downloads the image on
20
+ * its own, which is why `imageUrl` has to be reachable from the internet.
21
+ */
22
+ async publishToInstagram(imageUrl, caption = "", type = "feed") {
23
+ const igId = this.credentials.instagramUserId;
24
+ if (!igId) throw new Error("MetaService: instagramUserId is not configured");
25
+ const container = await this.post(`${igId}/media`, {
26
+ image_url: imageUrl,
27
+ // Stories take no caption — Instagram ignores it, so it is not sent.
28
+ ...type === "stories" ? { media_type: "STORIES" } : { caption }
29
+ });
30
+ await this.waitForContainer(container.id);
31
+ const published = await this.post(`${igId}/media_publish`, {
32
+ creation_id: container.id
33
+ });
34
+ return published.id;
35
+ }
36
+ /** Publishes a photo to the Page feed. Returns the post id. */
37
+ async publishToPage(imageUrl, message = "") {
38
+ const pageId = this.requirePage();
39
+ const published = await this.post(`${pageId}/photos`, { url: imageUrl, message });
40
+ return _nullishCoalesce(published.post_id, () => ( published.id));
41
+ }
42
+ /**
43
+ * Publishes a photo story to the Page.
44
+ *
45
+ * Not the same endpoint as a feed post: stories take two steps. The photo is
46
+ * uploaded **unpublished** — publishing it would also put it on the wall,
47
+ * which is not what a story is — and the story is created from the id it
48
+ * returns. Page stories carry no caption, so anything that must be read has
49
+ * to live inside the image.
50
+ */
51
+ async publishStoryToPage(imageUrl) {
52
+ const pageId = this.requirePage();
53
+ const photo = await this.post(`${pageId}/photos`, { url: imageUrl, published: "false" });
54
+ if (!photo.id) throw new Error("MetaService: Facebook returned no photo id");
55
+ const story = await this.post(`${pageId}/photo_stories`, { photo_id: photo.id });
56
+ return _nullishCoalesce(_nullishCoalesce(story.post_id, () => ( story.id)), () => ( photo.id));
57
+ }
58
+ /**
59
+ * Checks the credentials before anything is published.
60
+ *
61
+ * Worth calling first: this API fails late. With an expired token or a
62
+ * misconfigured account the error surfaces after the image is already hosted
63
+ * and half the batch is out.
64
+ */
65
+ async describe() {
66
+ const me = await this.get("me?fields=id,name");
67
+ const out = {
68
+ token: _nullishCoalesce(me.name, () => ( me.id))
69
+ };
70
+ if (this.credentials.pageId) {
71
+ out.page = (await this.get(`${this.credentials.pageId}?fields=name`)).name;
72
+ }
73
+ if (this.credentials.instagramUserId) {
74
+ out.instagram = (await this.get(`${this.credentials.instagramUserId}?fields=username`)).username;
75
+ }
76
+ return out;
77
+ }
78
+ requirePage() {
79
+ const pageId = this.credentials.pageId;
80
+ if (!pageId) throw new Error("MetaService: pageId is not configured");
81
+ return pageId;
82
+ }
83
+ /**
84
+ * Waits until Meta has finished fetching and processing the container.
85
+ *
86
+ * For a plain image it is usually instant, but not always, and publishing a
87
+ * container still in IN_PROGRESS returns an error that reads like a
88
+ * permissions problem and is not one.
89
+ */
90
+ async waitForContainer(id, attempts = 20) {
91
+ for (let i = 0; i < attempts; i++) {
92
+ const { status_code } = await this.get(`${id}?fields=status_code`);
93
+ if (status_code === "FINISHED") return;
94
+ if (status_code === "ERROR") {
95
+ throw new Error("MetaService: Meta could not process the image \u2014 is the URL reachable?");
96
+ }
97
+ await new Promise((resolve) => setTimeout(resolve, 3e3));
98
+ }
99
+ throw new Error("MetaService: the Instagram container never finished processing");
100
+ }
101
+ async post(path, body) {
102
+ const response = await fetch(`${GRAPH}/${path}`, {
103
+ method: "POST",
104
+ headers: { "Content-Type": "application/json" },
105
+ body: JSON.stringify({ ...body, access_token: this.credentials.accessToken })
106
+ });
107
+ return this.unwrap(await response.json());
108
+ }
109
+ async get(path) {
110
+ const separator = path.includes("?") ? "&" : "?";
111
+ const response = await fetch(
112
+ `${GRAPH}/${path}${separator}access_token=${this.credentials.accessToken}`
113
+ );
114
+ return this.unwrap(await response.json());
115
+ }
116
+ /**
117
+ * Meta's own messages are cryptic, so the two failures you will actually hit
118
+ * carry the likely cause appended.
119
+ */
120
+ unwrap(json) {
121
+ if (!json.error) return json;
122
+ const { code, type, message } = json.error;
123
+ const hint = type === "OAuthException" ? " \u2014 usually an expired token, or one missing the publishing permissions." : code === 100 ? " \u2014 check the token is a Page token and the Instagram account is a Business one linked to the Page." : "";
124
+ throw new Error(`Meta (${code}): ${message}${hint}`);
125
+ }
126
+ };
127
+ MetaService = exports.MetaService = _chunk2REOCMUDcjs.__decorateClass.call(void 0, [
128
+ _common.Injectable.call(void 0, )
129
+ ], MetaService);
130
+
131
+ // src/meta/meta.module.ts
132
+ var MetaModule = class {
133
+ static forRoot(credentials) {
134
+ return {
135
+ module: MetaModule,
136
+ providers: [
137
+ {
138
+ provide: MetaService,
139
+ useFactory: () => {
140
+ const resolved = _nullishCoalesce(credentials, () => ( {
141
+ accessToken: _nullishCoalesce(process.env.META_ACCESS_TOKEN, () => ( "")),
142
+ pageId: process.env.META_PAGE_ID,
143
+ instagramUserId: process.env.META_IG_USER_ID
144
+ }));
145
+ if (!resolved.accessToken) {
146
+ throw new Error(
147
+ "MetaModule: no access token. Set META_ACCESS_TOKEN or pass credentials to forRoot()."
148
+ );
149
+ }
150
+ return new MetaService(resolved);
151
+ }
152
+ }
153
+ ],
154
+ exports: [MetaService]
155
+ };
156
+ }
157
+ };
158
+ MetaModule = exports.MetaModule = _chunk2REOCMUDcjs.__decorateClass.call(void 0, [
159
+ _common.Module.call(void 0, {})
160
+ ], MetaModule);
161
+
162
+
163
+
164
+ exports.MetaModule = MetaModule; exports.MetaService = MetaService;
@@ -0,0 +1,3 @@
1
+ export { MetaModule } from './meta.module.js';
2
+ export { MetaService } from './meta.service.js';
3
+ export type { MetaCredentials, InstagramMediaType } from './meta.service.js';
@@ -0,0 +1,164 @@
1
+ import {
2
+ __decorateClass
3
+ } from "../chunk-4MGIQFAJ.js";
4
+
5
+ // src/meta/meta.module.ts
6
+ import { Module } from "@nestjs/common";
7
+
8
+ // src/meta/meta.service.ts
9
+ import { Injectable } from "@nestjs/common";
10
+ var GRAPH = "https://graph.facebook.com/v21.0";
11
+ var MetaService = class {
12
+ constructor(credentials) {
13
+ this.credentials = credentials;
14
+ }
15
+ /**
16
+ * Publishes an image to Instagram. Returns the media id.
17
+ *
18
+ * Instagram publishes in two steps: a container is created with the image and
19
+ * caption, and then published. Between the two Meta downloads the image on
20
+ * its own, which is why `imageUrl` has to be reachable from the internet.
21
+ */
22
+ async publishToInstagram(imageUrl, caption = "", type = "feed") {
23
+ const igId = this.credentials.instagramUserId;
24
+ if (!igId) throw new Error("MetaService: instagramUserId is not configured");
25
+ const container = await this.post(`${igId}/media`, {
26
+ image_url: imageUrl,
27
+ // Stories take no caption — Instagram ignores it, so it is not sent.
28
+ ...type === "stories" ? { media_type: "STORIES" } : { caption }
29
+ });
30
+ await this.waitForContainer(container.id);
31
+ const published = await this.post(`${igId}/media_publish`, {
32
+ creation_id: container.id
33
+ });
34
+ return published.id;
35
+ }
36
+ /** Publishes a photo to the Page feed. Returns the post id. */
37
+ async publishToPage(imageUrl, message = "") {
38
+ const pageId = this.requirePage();
39
+ const published = await this.post(`${pageId}/photos`, { url: imageUrl, message });
40
+ return published.post_id ?? published.id;
41
+ }
42
+ /**
43
+ * Publishes a photo story to the Page.
44
+ *
45
+ * Not the same endpoint as a feed post: stories take two steps. The photo is
46
+ * uploaded **unpublished** — publishing it would also put it on the wall,
47
+ * which is not what a story is — and the story is created from the id it
48
+ * returns. Page stories carry no caption, so anything that must be read has
49
+ * to live inside the image.
50
+ */
51
+ async publishStoryToPage(imageUrl) {
52
+ const pageId = this.requirePage();
53
+ const photo = await this.post(`${pageId}/photos`, { url: imageUrl, published: "false" });
54
+ if (!photo.id) throw new Error("MetaService: Facebook returned no photo id");
55
+ const story = await this.post(`${pageId}/photo_stories`, { photo_id: photo.id });
56
+ return story.post_id ?? story.id ?? photo.id;
57
+ }
58
+ /**
59
+ * Checks the credentials before anything is published.
60
+ *
61
+ * Worth calling first: this API fails late. With an expired token or a
62
+ * misconfigured account the error surfaces after the image is already hosted
63
+ * and half the batch is out.
64
+ */
65
+ async describe() {
66
+ const me = await this.get("me?fields=id,name");
67
+ const out = {
68
+ token: me.name ?? me.id
69
+ };
70
+ if (this.credentials.pageId) {
71
+ out.page = (await this.get(`${this.credentials.pageId}?fields=name`)).name;
72
+ }
73
+ if (this.credentials.instagramUserId) {
74
+ out.instagram = (await this.get(`${this.credentials.instagramUserId}?fields=username`)).username;
75
+ }
76
+ return out;
77
+ }
78
+ requirePage() {
79
+ const pageId = this.credentials.pageId;
80
+ if (!pageId) throw new Error("MetaService: pageId is not configured");
81
+ return pageId;
82
+ }
83
+ /**
84
+ * Waits until Meta has finished fetching and processing the container.
85
+ *
86
+ * For a plain image it is usually instant, but not always, and publishing a
87
+ * container still in IN_PROGRESS returns an error that reads like a
88
+ * permissions problem and is not one.
89
+ */
90
+ async waitForContainer(id, attempts = 20) {
91
+ for (let i = 0; i < attempts; i++) {
92
+ const { status_code } = await this.get(`${id}?fields=status_code`);
93
+ if (status_code === "FINISHED") return;
94
+ if (status_code === "ERROR") {
95
+ throw new Error("MetaService: Meta could not process the image \u2014 is the URL reachable?");
96
+ }
97
+ await new Promise((resolve) => setTimeout(resolve, 3e3));
98
+ }
99
+ throw new Error("MetaService: the Instagram container never finished processing");
100
+ }
101
+ async post(path, body) {
102
+ const response = await fetch(`${GRAPH}/${path}`, {
103
+ method: "POST",
104
+ headers: { "Content-Type": "application/json" },
105
+ body: JSON.stringify({ ...body, access_token: this.credentials.accessToken })
106
+ });
107
+ return this.unwrap(await response.json());
108
+ }
109
+ async get(path) {
110
+ const separator = path.includes("?") ? "&" : "?";
111
+ const response = await fetch(
112
+ `${GRAPH}/${path}${separator}access_token=${this.credentials.accessToken}`
113
+ );
114
+ return this.unwrap(await response.json());
115
+ }
116
+ /**
117
+ * Meta's own messages are cryptic, so the two failures you will actually hit
118
+ * carry the likely cause appended.
119
+ */
120
+ unwrap(json) {
121
+ if (!json.error) return json;
122
+ const { code, type, message } = json.error;
123
+ const hint = type === "OAuthException" ? " \u2014 usually an expired token, or one missing the publishing permissions." : code === 100 ? " \u2014 check the token is a Page token and the Instagram account is a Business one linked to the Page." : "";
124
+ throw new Error(`Meta (${code}): ${message}${hint}`);
125
+ }
126
+ };
127
+ MetaService = __decorateClass([
128
+ Injectable()
129
+ ], MetaService);
130
+
131
+ // src/meta/meta.module.ts
132
+ var MetaModule = class {
133
+ static forRoot(credentials) {
134
+ return {
135
+ module: MetaModule,
136
+ providers: [
137
+ {
138
+ provide: MetaService,
139
+ useFactory: () => {
140
+ const resolved = credentials ?? {
141
+ accessToken: process.env.META_ACCESS_TOKEN ?? "",
142
+ pageId: process.env.META_PAGE_ID,
143
+ instagramUserId: process.env.META_IG_USER_ID
144
+ };
145
+ if (!resolved.accessToken) {
146
+ throw new Error(
147
+ "MetaModule: no access token. Set META_ACCESS_TOKEN or pass credentials to forRoot()."
148
+ );
149
+ }
150
+ return new MetaService(resolved);
151
+ }
152
+ }
153
+ ],
154
+ exports: [MetaService]
155
+ };
156
+ }
157
+ };
158
+ MetaModule = __decorateClass([
159
+ Module({})
160
+ ], MetaModule);
161
+ export {
162
+ MetaModule,
163
+ MetaService
164
+ };
@@ -0,0 +1,16 @@
1
+ import { type DynamicModule } from '@nestjs/common';
2
+ import { type MetaCredentials } from './meta.service.js';
3
+ /**
4
+ * Outbound client for Instagram and Facebook Pages.
5
+ *
6
+ * Send-only, like `TelegramModule`: it publishes, it does not read insights,
7
+ * handle webhooks or manage comments.
8
+ *
9
+ * Credentials come from the environment by default —`META_ACCESS_TOKEN`,
10
+ * `META_PAGE_ID`, `META_IG_USER_ID`— or can be passed explicitly with
11
+ * `forRoot`, which is what you want when one process publishes to several
12
+ * accounts.
13
+ */
14
+ export declare class MetaModule {
15
+ static forRoot(credentials?: MetaCredentials): DynamicModule;
16
+ }
@@ -0,0 +1,81 @@
1
+ /** Instagram accepts these three. Carousels need a different flow. */
2
+ export type InstagramMediaType = 'feed' | 'stories' | 'reels';
3
+ export interface MetaCredentials {
4
+ /** Page access token, not a user token. See the note on `publishToPage`. */
5
+ accessToken: string;
6
+ /** Facebook Page id. Required for anything Facebook. */
7
+ pageId?: string;
8
+ /** Instagram Business account id, obtained from the Page. */
9
+ instagramUserId?: string;
10
+ }
11
+ /**
12
+ * Publishes to Instagram and Facebook Pages through Meta's Graph API.
13
+ *
14
+ * Two things about this API drive the whole design and are worth knowing before
15
+ * you use it:
16
+ *
17
+ * - **Images are never uploaded.** You pass a publicly reachable URL and Meta
18
+ * fetches it. It only needs that URL while publishing — afterwards it keeps
19
+ * its own copy and serves the media from its CDN — so a short-lived host is
20
+ * enough. `MediaModule` in this same package exists for exactly that.
21
+ * - **Publishing to a Page requires a Page token, not a user token.** With a
22
+ * user token Meta assumes you are posting as a person and asks for
23
+ * `publish_actions`, a permission it removed in 2018, so the error you get
24
+ * points nowhere near the real cause.
25
+ *
26
+ * Required permissions: `instagram_basic` and `instagram_content_publish` for
27
+ * Instagram, `pages_manage_posts` for Facebook. Publishing to your own Pages
28
+ * needs no App Review; publishing on behalf of third parties does.
29
+ */
30
+ export declare class MetaService {
31
+ private readonly credentials;
32
+ constructor(credentials: MetaCredentials);
33
+ /**
34
+ * Publishes an image to Instagram. Returns the media id.
35
+ *
36
+ * Instagram publishes in two steps: a container is created with the image and
37
+ * caption, and then published. Between the two Meta downloads the image on
38
+ * its own, which is why `imageUrl` has to be reachable from the internet.
39
+ */
40
+ publishToInstagram(imageUrl: string, caption?: string, type?: InstagramMediaType): Promise<string>;
41
+ /** Publishes a photo to the Page feed. Returns the post id. */
42
+ publishToPage(imageUrl: string, message?: string): Promise<string>;
43
+ /**
44
+ * Publishes a photo story to the Page.
45
+ *
46
+ * Not the same endpoint as a feed post: stories take two steps. The photo is
47
+ * uploaded **unpublished** — publishing it would also put it on the wall,
48
+ * which is not what a story is — and the story is created from the id it
49
+ * returns. Page stories carry no caption, so anything that must be read has
50
+ * to live inside the image.
51
+ */
52
+ publishStoryToPage(imageUrl: string): Promise<string>;
53
+ /**
54
+ * Checks the credentials before anything is published.
55
+ *
56
+ * Worth calling first: this API fails late. With an expired token or a
57
+ * misconfigured account the error surfaces after the image is already hosted
58
+ * and half the batch is out.
59
+ */
60
+ describe(): Promise<{
61
+ token: string;
62
+ page?: string;
63
+ instagram?: string;
64
+ }>;
65
+ private requirePage;
66
+ /**
67
+ * Waits until Meta has finished fetching and processing the container.
68
+ *
69
+ * For a plain image it is usually instant, but not always, and publishing a
70
+ * container still in IN_PROGRESS returns an error that reads like a
71
+ * permissions problem and is not one.
72
+ */
73
+ private waitForContainer;
74
+ private post;
75
+ private get;
76
+ /**
77
+ * Meta's own messages are cryptic, so the two failures you will actually hit
78
+ * carry the likely cause appended.
79
+ */
80
+ private unwrap;
81
+ }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@miguelmorales13/nestkit",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "type": "module",
7
- "description": "Reusable NestJS building blocks: i18n, error handling, standardized responses, request tracking, base entities, generic CRUD, Postgres/Supabase connection helpers, outbound Telegram/WhatsApp/Stripe clients, batteries-included auth, Stripe webhooks, and Resend/Nodemailer email.",
7
+ "description": "Reusable NestJS building blocks: i18n, error handling, standardized responses, request tracking, base entities, generic CRUD, Postgres/Supabase connection helpers, outbound Telegram/WhatsApp/Stripe/Meta clients, short-lived media hosting, batteries-included auth, Stripe webhooks, and Resend/Nodemailer email.",
8
8
  "publishConfig": {
9
9
  "access": "public"
10
10
  },
@@ -100,6 +100,16 @@
100
100
  "types": "./dist/bootstrap/index.d.ts",
101
101
  "import": "./dist/bootstrap/index.js",
102
102
  "require": "./dist/bootstrap/index.cjs"
103
+ },
104
+ "./meta": {
105
+ "types": "./dist/meta/index.d.ts",
106
+ "import": "./dist/meta/index.js",
107
+ "require": "./dist/meta/index.cjs"
108
+ },
109
+ "./media": {
110
+ "types": "./dist/media/index.d.ts",
111
+ "import": "./dist/media/index.js",
112
+ "require": "./dist/media/index.cjs"
103
113
  }
104
114
  },
105
115
  "scripts": {
@@ -1,9 +1,9 @@
1
- import {
2
- AppException
3
- } from "./chunk-YFYHLYHN.js";
4
1
  import {
5
2
  RequestContext
6
3
  } from "./chunk-EBO6UKHL.js";
4
+ import {
5
+ AppException
6
+ } from "./chunk-YFYHLYHN.js";
7
7
  import {
8
8
  __decorateClass
9
9
  } from "./chunk-4MGIQFAJ.js";
@@ -1,9 +1,9 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
2
2
 
3
- var _chunkR7BVS6CIcjs = require('./chunk-R7BVS6CI.cjs');
3
+ var _chunkZA56XBCKcjs = require('./chunk-ZA56XBCK.cjs');
4
4
 
5
5
 
6
- var _chunkZA56XBCKcjs = require('./chunk-ZA56XBCK.cjs');
6
+ var _chunkR7BVS6CIcjs = require('./chunk-R7BVS6CI.cjs');
7
7
 
8
8
 
9
9
  var _chunk2REOCMUDcjs = require('./chunk-2REOCMUD.cjs');