@vsaas/loopback 11.0.3 → 11.2.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
@@ -69,6 +69,83 @@ The supported application-facing surface intentionally includes:
69
69
 
70
70
  Those legacy-looking `.js` subpaths are still supported for compatibility, but they now resolve to compiled files in `dist/` via `package.json` exports.
71
71
 
72
+ ## Email Transport Configuration
73
+
74
+ The built-in `email` datasource continues to use Nodemailer under the hood.
75
+
76
+ Built-in Nodemailer transport types supported directly by this package:
77
+
78
+ - `smtp`
79
+ - `sendmail`
80
+ - `ses`
81
+ - `stream`
82
+ - `json`
83
+
84
+ LoopBack compatibility transport types also preserved by this fork:
85
+
86
+ - `direct`
87
+ - `stub`
88
+
89
+ Unknown transport types still follow the legacy plugin convention and try to load `nodemailer-<type>-transport`. For example, `type: 'mailgun'` expects `nodemailer-mailgun-transport` to be installed. This means supported built-ins do not require extra transport packages, while typos or unsupported custom types still fail fast with a module resolution error.
90
+
91
+ ### SMTP example
92
+
93
+ ```js
94
+ app.dataSource('email', {
95
+ connector: loopback.Mail,
96
+ transport: {
97
+ type: 'smtp',
98
+ host: 'smtp.example.com',
99
+ port: 587,
100
+ secure: false,
101
+ auth: {
102
+ user: process.env.SMTP_USER,
103
+ pass: process.env.SMTP_PASSWORD,
104
+ },
105
+ },
106
+ });
107
+ ```
108
+
109
+ ### SES example
110
+
111
+ `type: 'ses'` uses Nodemailer's built-in SES transport with `@aws-sdk/client-sesv2`. You can either pass a prebuilt `SES` config, provide `sesClient` plus `SendEmailCommand`, or rely on the default AWS credential chain.
112
+
113
+ ```js
114
+ app.dataSource('email', {
115
+ connector: loopback.Mail,
116
+ transport: {
117
+ type: 'ses',
118
+ region: process.env.AWS_REGION,
119
+ },
120
+ });
121
+ ```
122
+
123
+ ### Stream and JSON examples
124
+
125
+ These transports are useful for tests, previews, or custom delivery pipelines because they generate the message without sending it.
126
+
127
+ ```js
128
+ app.dataSource('email', {
129
+ connector: loopback.Mail,
130
+ transport: {
131
+ type: 'stream',
132
+ buffer: true,
133
+ },
134
+ });
135
+ ```
136
+
137
+ ```js
138
+ app.dataSource('email', {
139
+ connector: loopback.Mail,
140
+ transport: {
141
+ type: 'json',
142
+ skipEncoding: true,
143
+ },
144
+ });
145
+ ```
146
+
147
+ All other Nodemailer transport-specific options are passed through to `nodemailer.createTransport()` after the LoopBack-specific `type` and `alias` fields are normalized.
148
+
72
149
  ## Notes for Fork Users
73
150
 
74
151
  - This is a maintained fork, not the upstream package.
@@ -39,7 +39,10 @@ var require_mail = /* @__PURE__ */ require_runtime$1.__commonJSMin(((exports, mo
39
39
  if (transportType === "smtp") transport = mailer.createTransport(setting);
40
40
  else if (transportType === "direct") transport = mailer.createTransport(createDirectTransport(setting));
41
41
  else if (transportType === "stub") transport = mailer.createTransport(createStubTransport(setting));
42
- else if (transportType === "ses" && supportsBuiltinSesTransport(setting)) transport = mailer.createTransport(createBuiltinSesTransportConfig(setting));
42
+ else if (transportType === "sendmail") transport = mailer.createTransport(createBuiltinTransportConfig(setting, "sendmail"));
43
+ else if (transportType === "ses") transport = mailer.createTransport(createBuiltinSesTransportConfig(setting));
44
+ else if (transportType === "stream") transport = mailer.createTransport(createBuiltinTransportConfig(setting, "streamTransport"));
45
+ else if (transportType === "json") transport = mailer.createTransport(createBuiltinTransportConfig(setting, "jsonTransport"));
43
46
  else {
44
47
  const transportModuleName = "nodemailer-" + transportType + "-transport";
45
48
  const transportModule = require(transportModuleName);
@@ -48,13 +51,15 @@ var require_mail = /* @__PURE__ */ require_runtime$1.__commonJSMin(((exports, mo
48
51
  connector.transportsIndex[setting.alias || setting.type] = transport;
49
52
  connector.transports.push(transport);
50
53
  };
51
- function supportsBuiltinSesTransport(setting) {
52
- return !!(setting.SES || setting.sesClient && setting.SendEmailCommand || hasLegacySesCredentials(setting));
53
- }
54
- function createBuiltinSesTransportConfig(setting) {
54
+ function createBuiltinTransportConfig(setting, transportKey) {
55
55
  const transportConfig = Object.assign({}, setting);
56
56
  delete transportConfig.alias;
57
57
  delete transportConfig.type;
58
+ if (transportKey) transportConfig[transportKey] = true;
59
+ return transportConfig;
60
+ }
61
+ function createBuiltinSesTransportConfig(setting) {
62
+ const transportConfig = createBuiltinTransportConfig(setting);
58
63
  if (!transportConfig.SES) transportConfig.SES = createSesTransportOptions(transportConfig);
59
64
  delete transportConfig.sesClient;
60
65
  delete transportConfig.SendEmailCommand;
@@ -64,22 +69,21 @@ var require_mail = /* @__PURE__ */ require_runtime$1.__commonJSMin(((exports, mo
64
69
  delete transportConfig.region;
65
70
  return transportConfig;
66
71
  }
67
- function hasLegacySesCredentials(setting) {
68
- return !!(setting && setting.accessKeyId && setting.secretAccessKey && setting.region);
69
- }
70
72
  function createSesTransportOptions(setting) {
71
73
  if (setting.sesClient && setting.SendEmailCommand) return {
72
74
  sesClient: setting.sesClient,
73
75
  SendEmailCommand: setting.SendEmailCommand
74
76
  };
75
- if (!hasLegacySesCredentials(setting)) return setting.SES;
77
+ if (setting.SES) return setting.SES;
76
78
  const ses = require("@aws-sdk/client-sesv2");
77
- const clientConfig = { region: setting.region };
78
- clientConfig.credentials = {
79
- accessKeyId: setting.accessKeyId,
80
- secretAccessKey: setting.secretAccessKey
81
- };
82
- if (setting.sessionToken) clientConfig.credentials.sessionToken = setting.sessionToken;
79
+ const clientConfig = {};
80
+ if (setting.region) clientConfig.region = setting.region;
81
+ if (setting.accessKeyId || setting.secretAccessKey || setting.sessionToken) {
82
+ clientConfig.credentials = {};
83
+ if (setting.accessKeyId) clientConfig.credentials.accessKeyId = setting.accessKeyId;
84
+ if (setting.secretAccessKey) clientConfig.credentials.secretAccessKey = setting.secretAccessKey;
85
+ if (setting.sessionToken) clientConfig.credentials.sessionToken = setting.sessionToken;
86
+ }
83
87
  return {
84
88
  sesClient: new ses.SESv2Client(clientConfig),
85
89
  SendEmailCommand: ses.SendEmailCommand
@@ -14,6 +14,31 @@ var require_model = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, mod
14
14
  const SharedClass = require("@vsaas/remoting").SharedClass;
15
15
  const utils = require_lib_utils;
16
16
  const deprecated = require("depd")("loopback");
17
+ function completeSyncRemoteHook(retval, next) {
18
+ if (retval && typeof retval.then === "function") {
19
+ retval.then(function() {
20
+ next();
21
+ return null;
22
+ }, next);
23
+ return;
24
+ }
25
+ next();
26
+ }
27
+ function invokeSyncRemoteHook(fn, ctx, result, next) {
28
+ let hasResult = true;
29
+ if (next === void 0) {
30
+ next = result;
31
+ result = void 0;
32
+ hasResult = false;
33
+ }
34
+ let retval;
35
+ try {
36
+ retval = hasResult ? fn(ctx, result) : fn(ctx);
37
+ } catch (err) {
38
+ return next(err);
39
+ }
40
+ completeSyncRemoteHook(retval, next);
41
+ }
17
42
  module.exports = function(registry) {
18
43
  /**
19
44
  * The base class for **all models**.
@@ -188,6 +213,14 @@ var require_model = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, mod
188
213
  });
189
214
  });
190
215
  };
216
+ ModelCtor.beforeRemoteSync = function(name, fn) {
217
+ const className = this.modelName;
218
+ this._runWhenAttachedToApp(function(app) {
219
+ app.remotes().before(className + "." + name, function(ctx, next) {
220
+ invokeSyncRemoteHook(fn, ctx, next);
221
+ });
222
+ });
223
+ };
191
224
  ModelCtor.afterRemote = function(name, fn) {
192
225
  const className = this.modelName;
193
226
  this._runWhenAttachedToApp(function(app) {
@@ -196,12 +229,28 @@ var require_model = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, mod
196
229
  });
197
230
  });
198
231
  };
232
+ ModelCtor.afterRemoteSync = function(name, fn) {
233
+ const className = this.modelName;
234
+ this._runWhenAttachedToApp(function(app) {
235
+ app.remotes().after(className + "." + name, function(ctx, next) {
236
+ invokeSyncRemoteHook(fn, ctx, ctx.result, next);
237
+ });
238
+ });
239
+ };
199
240
  ModelCtor.afterRemoteError = function(name, fn) {
200
241
  const className = this.modelName;
201
242
  this._runWhenAttachedToApp(function(app) {
202
243
  app.remotes().afterError(className + "." + name, fn);
203
244
  });
204
245
  };
246
+ ModelCtor.afterRemoteErrorSync = function(name, fn) {
247
+ const className = this.modelName;
248
+ this._runWhenAttachedToApp(function(app) {
249
+ app.remotes().afterError(className + "." + name, function(ctx, next) {
250
+ invokeSyncRemoteHook(fn, ctx, next);
251
+ });
252
+ });
253
+ };
205
254
  ModelCtor._runWhenAttachedToApp = function(fn) {
206
255
  if (typeof fn === "function") {
207
256
  if (this.app) return fn(this.app);
package/dist/package.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
  var require_package = /* @__PURE__ */ require("./_virtual/_rolldown/runtime.cjs").__commonJSMin(((exports, module) => {
3
3
  module.exports = {
4
4
  "name": "@vsaas/loopback",
5
- "version": "11.0.3",
5
+ "version": "11.2.0",
6
6
  "description": "Fork of LoopBack 3 framework for building APIs and microservices in Node.js",
7
7
  "keywords": [
8
8
  "StrongLoop",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vsaas/loopback",
3
- "version": "11.0.3",
3
+ "version": "11.2.0",
4
4
  "description": "Fork of LoopBack 3 framework for building APIs and microservices in Node.js",
5
5
  "keywords": [
6
6
  "StrongLoop",
@@ -69,8 +69,8 @@
69
69
  "nodemailer": "^8.0.4",
70
70
  "serve-favicon": "^2.2.0",
71
71
  "@vsaas/loopback-connector-remote": "^11.0.2",
72
- "@vsaas/loopback-datasource-juggler": "^11.0.2",
73
- "@vsaas/remoting": "^11.0.2"
72
+ "@vsaas/remoting": "^11.0.2",
73
+ "@vsaas/loopback-datasource-juggler": "^11.1.0"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@types/node": "25.5.0",
@@ -87,9 +87,9 @@
87
87
  "supertest": "7.2.2",
88
88
  "tsdown": "0.21.7",
89
89
  "typescript": "6.0.2",
90
- "vitest": "4.1.1",
91
- "@vsaas/loopback-boot": "^11.0.2",
92
- "@vsaas/error-handler": "^11.0.3"
90
+ "vitest": "4.1.2",
91
+ "@vsaas/error-handler": "^11.0.5",
92
+ "@vsaas/loopback-boot": "^11.0.2"
93
93
  },
94
94
  "engines": {
95
95
  "node": ">=20"