@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 +77 -0
- package/dist/lib/connectors/mail.cjs +19 -15
- package/dist/lib/model.cjs +49 -0
- package/dist/package.cjs +1 -1
- package/package.json +6 -6
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 === "
|
|
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
|
|
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 (
|
|
77
|
+
if (setting.SES) return setting.SES;
|
|
76
78
|
const ses = require("@aws-sdk/client-sesv2");
|
|
77
|
-
const clientConfig = {
|
|
78
|
-
clientConfig.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
package/dist/lib/model.cjs
CHANGED
|
@@ -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
|
|
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
|
+
"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/
|
|
73
|
-
"@vsaas/
|
|
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.
|
|
91
|
-
"@vsaas/
|
|
92
|
-
"@vsaas/
|
|
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"
|