toiljs 0.0.44 → 0.0.46
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/CHANGELOG.md +9 -0
- package/RSG.md +105 -27
- package/build/cli/.tsbuildinfo +1 -1
- package/build/cli/index.js +12 -2
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/compiler/config.d.ts +4 -0
- package/build/compiler/config.js +1 -0
- package/build/compiler/email-preview.d.ts +12 -0
- package/build/compiler/email-preview.js +253 -0
- package/build/compiler/emails.d.ts +6 -3
- package/build/compiler/emails.js +52 -12
- package/build/compiler/index.js +15 -0
- package/build/compiler/plugin.js +64 -2
- package/build/compiler/vite.js +1 -0
- package/build/devserver/.tsbuildinfo +1 -1
- package/build/devserver/dotenv.d.ts +8 -0
- package/build/devserver/dotenv.js +59 -0
- package/build/devserver/email/caps.d.ts +9 -0
- package/build/devserver/email/caps.js +0 -0
- package/build/devserver/email/config.d.ts +21 -0
- package/build/devserver/email/config.js +72 -0
- package/build/devserver/email/index.d.ts +25 -0
- package/build/devserver/email/index.js +57 -0
- package/build/devserver/email/providers.d.ts +12 -0
- package/build/devserver/email/providers.js +96 -0
- package/build/devserver/email/status.d.ts +10 -0
- package/build/devserver/email/status.js +11 -0
- package/build/devserver/email/validate.d.ts +2 -0
- package/build/devserver/email/validate.js +24 -0
- package/build/devserver/email/wire.d.ts +8 -0
- package/build/devserver/email/wire.js +32 -0
- package/build/devserver/env.js +5 -54
- package/build/devserver/host.js +22 -7
- package/build/devserver/index.d.ts +2 -0
- package/build/devserver/index.js +8 -0
- package/build/shared/.tsbuildinfo +1 -1
- package/build/shared/index.d.ts +13 -0
- package/docs/email.md +64 -22
- package/package.json +4 -2
- package/src/cli/create.ts +2 -2
- package/src/cli/doctor.ts +15 -0
- package/src/compiler/config.ts +14 -0
- package/src/compiler/email-preview.ts +305 -0
- package/src/compiler/emails.ts +82 -12
- package/src/compiler/index.ts +20 -0
- package/src/compiler/plugin.ts +88 -4
- package/src/compiler/vite.ts +4 -0
- package/src/devserver/dotenv.ts +94 -0
- package/src/devserver/email/caps.ts +0 -0
- package/src/devserver/email/config.ts +123 -0
- package/src/devserver/email/index.ts +111 -0
- package/src/devserver/email/providers.ts +130 -0
- package/src/devserver/email/status.ts +23 -0
- package/src/devserver/email/validate.ts +40 -0
- package/src/devserver/email/wire.ts +55 -0
- package/src/devserver/env.ts +8 -65
- package/src/devserver/host.ts +29 -12
- package/src/devserver/index.ts +20 -0
- package/src/shared/index.ts +36 -0
- package/test/devserver-email.test.ts +241 -0
- package/test/email-preview.test.ts +68 -0
- package/test/emails.test.ts +58 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { EmailStatus } from './status.js';
|
|
2
|
+
const MAX_ATTEMPTS = 3;
|
|
3
|
+
const BACKOFF_BASE_MS = 200;
|
|
4
|
+
const POST_TIMEOUT_MS = 10_000;
|
|
5
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
6
|
+
function classifyHttp(status) {
|
|
7
|
+
if (status !== null && status >= 200 && status < 300)
|
|
8
|
+
return EmailStatus.Sent;
|
|
9
|
+
if (status !== null && status >= 400 && status < 500)
|
|
10
|
+
return EmailStatus.ProviderError;
|
|
11
|
+
return 'retry';
|
|
12
|
+
}
|
|
13
|
+
export async function sendResend(cfg, msg) {
|
|
14
|
+
const payload = {
|
|
15
|
+
from: msg.from,
|
|
16
|
+
to: [msg.to],
|
|
17
|
+
subject: msg.subject,
|
|
18
|
+
};
|
|
19
|
+
if (msg.body.length > 0)
|
|
20
|
+
payload.text = msg.body;
|
|
21
|
+
if (msg.html.length > 0)
|
|
22
|
+
payload.html = msg.html;
|
|
23
|
+
let backoff = BACKOFF_BASE_MS;
|
|
24
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
25
|
+
let status = null;
|
|
26
|
+
try {
|
|
27
|
+
const res = await fetch('https://api.resend.com/emails', {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: {
|
|
30
|
+
authorization: `Bearer ${cfg.apiKey}`,
|
|
31
|
+
'content-type': 'application/json',
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify(payload),
|
|
34
|
+
signal: AbortSignal.timeout(POST_TIMEOUT_MS),
|
|
35
|
+
});
|
|
36
|
+
status = res.status;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
status = null;
|
|
40
|
+
}
|
|
41
|
+
const verdict = classifyHttp(status);
|
|
42
|
+
if (verdict !== 'retry')
|
|
43
|
+
return verdict;
|
|
44
|
+
if (attempt === MAX_ATTEMPTS)
|
|
45
|
+
break;
|
|
46
|
+
await sleep(backoff);
|
|
47
|
+
backoff *= 2;
|
|
48
|
+
}
|
|
49
|
+
return EmailStatus.ProviderError;
|
|
50
|
+
}
|
|
51
|
+
export async function sendSmtp(cfg, msg) {
|
|
52
|
+
const smtp = cfg.smtp;
|
|
53
|
+
if (smtp === undefined)
|
|
54
|
+
return EmailStatus.ProviderError;
|
|
55
|
+
let transporter;
|
|
56
|
+
try {
|
|
57
|
+
const nodemailer = await import('nodemailer');
|
|
58
|
+
transporter = nodemailer.createTransport({
|
|
59
|
+
host: smtp.host,
|
|
60
|
+
port: smtp.port,
|
|
61
|
+
secure: smtp.port === 465,
|
|
62
|
+
auth: { user: smtp.user, pass: cfg.apiKey },
|
|
63
|
+
connectionTimeout: POST_TIMEOUT_MS,
|
|
64
|
+
greetingTimeout: POST_TIMEOUT_MS,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return EmailStatus.ProviderError;
|
|
69
|
+
}
|
|
70
|
+
let backoff = BACKOFF_BASE_MS;
|
|
71
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
72
|
+
try {
|
|
73
|
+
await transporter.sendMail({
|
|
74
|
+
from: msg.from,
|
|
75
|
+
to: msg.to,
|
|
76
|
+
subject: msg.subject,
|
|
77
|
+
text: msg.body.length > 0 ? msg.body : undefined,
|
|
78
|
+
html: msg.html.length > 0 ? msg.html : undefined,
|
|
79
|
+
});
|
|
80
|
+
return EmailStatus.Sent;
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
const code = e.responseCode;
|
|
84
|
+
if (typeof code === 'number' && code >= 500)
|
|
85
|
+
return EmailStatus.ProviderError;
|
|
86
|
+
if (attempt === MAX_ATTEMPTS)
|
|
87
|
+
break;
|
|
88
|
+
await sleep(backoff);
|
|
89
|
+
backoff *= 2;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return EmailStatus.ProviderError;
|
|
93
|
+
}
|
|
94
|
+
export function sendVia(cfg, msg) {
|
|
95
|
+
return cfg.provider === 'resend' ? sendResend(cfg, msg) : sendSmtp(cfg, msg);
|
|
96
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export var EmailStatus;
|
|
2
|
+
(function (EmailStatus) {
|
|
3
|
+
EmailStatus[EmailStatus["Sent"] = 0] = "Sent";
|
|
4
|
+
EmailStatus[EmailStatus["Disabled"] = 1] = "Disabled";
|
|
5
|
+
EmailStatus[EmailStatus["Budget"] = 2] = "Budget";
|
|
6
|
+
EmailStatus[EmailStatus["RecipientCapped"] = 3] = "RecipientCapped";
|
|
7
|
+
EmailStatus[EmailStatus["Deduped"] = 4] = "Deduped";
|
|
8
|
+
EmailStatus[EmailStatus["TryLater"] = 5] = "TryLater";
|
|
9
|
+
EmailStatus[EmailStatus["BadRecipient"] = 6] = "BadRecipient";
|
|
10
|
+
EmailStatus[EmailStatus["ProviderError"] = 7] = "ProviderError";
|
|
11
|
+
})(EmailStatus || (EmailStatus = {}));
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const FORBIDDEN = new Set(['\r', '\n', '\0', ',', ';', ' ', '\t', '<', '>', '"']);
|
|
2
|
+
export function validRecipient(s) {
|
|
3
|
+
if (s.length === 0 || Buffer.byteLength(s, 'utf8') > 320)
|
|
4
|
+
return false;
|
|
5
|
+
for (const ch of s) {
|
|
6
|
+
if (FORBIDDEN.has(ch))
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
const parts = s.split('@');
|
|
10
|
+
if (parts.length !== 2)
|
|
11
|
+
return false;
|
|
12
|
+
const [local, domain] = parts;
|
|
13
|
+
return (local.length > 0 &&
|
|
14
|
+
domain.includes('.') &&
|
|
15
|
+
!domain.startsWith('.') &&
|
|
16
|
+
!domain.endsWith('.'));
|
|
17
|
+
}
|
|
18
|
+
export function validFrom(s) {
|
|
19
|
+
return (Buffer.byteLength(s, 'utf8') <= 320 &&
|
|
20
|
+
s.includes('@') &&
|
|
21
|
+
!s.includes('\r') &&
|
|
22
|
+
!s.includes('\n') &&
|
|
23
|
+
!s.includes('\0'));
|
|
24
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const HEADER_LEN = 14;
|
|
2
|
+
export function parseEmailBlob(raw) {
|
|
3
|
+
if (raw.length < HEADER_LEN)
|
|
4
|
+
return null;
|
|
5
|
+
const toLen = raw.readUInt16LE(0);
|
|
6
|
+
const subjectLen = raw.readUInt16LE(2);
|
|
7
|
+
const purposeLen = raw.readUInt16LE(4);
|
|
8
|
+
const bodyLen = raw.readUInt32LE(6);
|
|
9
|
+
const htmlLen = raw.readUInt32LE(10);
|
|
10
|
+
const total = toLen + subjectLen + purposeLen + bodyLen + htmlLen;
|
|
11
|
+
if (HEADER_LEN + total !== raw.length)
|
|
12
|
+
return null;
|
|
13
|
+
let off = HEADER_LEN;
|
|
14
|
+
const take = (n) => {
|
|
15
|
+
const end = off + n;
|
|
16
|
+
const slice = raw.subarray(off, end);
|
|
17
|
+
const s = slice.toString('utf8');
|
|
18
|
+
if (Buffer.byteLength(s, 'utf8') !== n)
|
|
19
|
+
return null;
|
|
20
|
+
off = end;
|
|
21
|
+
return s;
|
|
22
|
+
};
|
|
23
|
+
const to = take(toLen);
|
|
24
|
+
const subject = take(subjectLen);
|
|
25
|
+
const purpose = take(purposeLen);
|
|
26
|
+
const body = take(bodyLen);
|
|
27
|
+
const html = take(htmlLen);
|
|
28
|
+
if (to === null || subject === null || purpose === null || body === null || html === null) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return { to, subject, purpose, body, html };
|
|
32
|
+
}
|
package/build/devserver/env.js
CHANGED
|
@@ -1,58 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
const RESERVED_PREFIX = 'TOIL_';
|
|
4
|
-
let cache = null;
|
|
5
|
-
function parseValue(rest) {
|
|
6
|
-
const q = rest[0];
|
|
7
|
-
if (q === '"' || q === "'") {
|
|
8
|
-
const end = rest.indexOf(q, 1);
|
|
9
|
-
return end < 0 ? rest.slice(1) : rest.slice(1, end);
|
|
10
|
-
}
|
|
11
|
-
const hash = rest.indexOf(' #');
|
|
12
|
-
return (hash < 0 ? rest : rest.slice(0, hash)).trimEnd();
|
|
13
|
-
}
|
|
14
|
-
function parseDotenv(text, into) {
|
|
15
|
-
for (const raw of text.split('\n')) {
|
|
16
|
-
let line = raw.trim();
|
|
17
|
-
if (line.length === 0 || line.startsWith('#'))
|
|
18
|
-
continue;
|
|
19
|
-
if (line.startsWith('export '))
|
|
20
|
-
line = line.slice('export '.length);
|
|
21
|
-
const eq = line.indexOf('=');
|
|
22
|
-
if (eq < 0)
|
|
23
|
-
continue;
|
|
24
|
-
const key = line.slice(0, eq).trim();
|
|
25
|
-
if (key.length === 0 || key.startsWith(RESERVED_PREFIX))
|
|
26
|
-
continue;
|
|
27
|
-
into.set(key, parseValue(line.slice(eq + 1).trim()));
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function readFileInto(file, into) {
|
|
31
|
-
try {
|
|
32
|
-
parseDotenv(fs.readFileSync(path.join(process.cwd(), file), 'utf8'), into);
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function load() {
|
|
38
|
-
if (cache)
|
|
39
|
-
return cache;
|
|
40
|
-
const vars = new Map();
|
|
41
|
-
const secrets = new Map();
|
|
42
|
-
for (const [k, v] of Object.entries(process.env)) {
|
|
43
|
-
if (typeof v === 'string' && !k.startsWith(RESERVED_PREFIX))
|
|
44
|
-
vars.set(k, v);
|
|
45
|
-
}
|
|
46
|
-
readFileInto('.env', vars);
|
|
47
|
-
readFileInto('.env.secrets', secrets);
|
|
48
|
-
cache = { vars, secrets };
|
|
49
|
-
return cache;
|
|
50
|
-
}
|
|
1
|
+
import { loadEnvFiles } from './dotenv.js';
|
|
51
2
|
export function devEnvGet(key) {
|
|
52
|
-
const
|
|
53
|
-
return
|
|
3
|
+
const v = loadEnvFiles(process.cwd()).vars.get(key);
|
|
4
|
+
return v === undefined ? null : v;
|
|
54
5
|
}
|
|
55
6
|
export function devEnvGetSecure(key) {
|
|
56
|
-
const
|
|
57
|
-
return
|
|
7
|
+
const v = loadEnvFiles(process.cwd()).secrets.get(key);
|
|
8
|
+
return v === undefined ? null : v;
|
|
58
9
|
}
|
package/build/devserver/host.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { buildCryptoImports, freshCryptoState } from './crypto.js';
|
|
2
|
+
import { EmailStatus, getEmailService } from './email/index.js';
|
|
3
|
+
import { parseEmailBlob } from './email/wire.js';
|
|
2
4
|
import { devEnvGet, devEnvGetSecure } from './env.js';
|
|
3
5
|
import { ratelimitCheck } from './ratelimit.js';
|
|
4
6
|
const MAX_TOTAL_HEADERS_BYTES = 64 * 1024;
|
|
@@ -111,14 +113,27 @@ export function buildHostImports(ref, state) {
|
|
|
111
113
|
},
|
|
112
114
|
email_send: (reqPtr, reqLen) => {
|
|
113
115
|
const raw = readBytes(ref, reqPtr, reqLen);
|
|
114
|
-
|
|
115
|
-
if (
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
const svc = getEmailService();
|
|
117
|
+
if (svc === null) {
|
|
118
|
+
const to = parseEmailBlob(raw)?.to ?? '<unparsed>';
|
|
119
|
+
process.stdout.write(` ✉ dev email_send -> ${to} (no email config; not sent)\n`);
|
|
120
|
+
return EmailStatus.Sent;
|
|
119
121
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
const { status, parsed } = svc.prepare(raw);
|
|
123
|
+
if (parsed === null) {
|
|
124
|
+
process.stdout.write(` ✉ dev email_send -> ${EmailStatus[status]}\n`);
|
|
125
|
+
return status;
|
|
126
|
+
}
|
|
127
|
+
void svc
|
|
128
|
+
.deliver(parsed)
|
|
129
|
+
.then((s) => {
|
|
130
|
+
const label = s === EmailStatus.Sent ? 'sent' : EmailStatus[s];
|
|
131
|
+
process.stdout.write(` ✉ dev email_send -> ${parsed.to} (${label})\n`);
|
|
132
|
+
})
|
|
133
|
+
.catch((e) => {
|
|
134
|
+
process.stdout.write(` ✉ dev email_send -> ${parsed.to} (error: ${String(e)})\n`);
|
|
135
|
+
});
|
|
136
|
+
return EmailStatus.Sent;
|
|
122
137
|
},
|
|
123
138
|
env_get: (keyPtr, keyLen, outPtr, outCap) => envLookup(ref, keyPtr, keyLen, outPtr, outCap, false),
|
|
124
139
|
env_get_secure: (keyPtr, keyLen, outPtr, outCap) => envLookup(ref, keyPtr, keyLen, outPtr, outCap, true),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { EmailBackendConfig } from 'toiljs/shared';
|
|
1
2
|
import { type ViteTarget } from './proxy.js';
|
|
2
3
|
export { METHOD_CODES, encodeRequestEnvelope, decodeResponseEnvelope, unpackHandleResult } from './envelope.js';
|
|
3
4
|
export type { EnvelopeRequest, EnvelopeResponse } from './envelope.js';
|
|
@@ -13,6 +14,7 @@ export interface DevServerOptions {
|
|
|
13
14
|
readonly wasmFile: string;
|
|
14
15
|
readonly vite: ViteTarget;
|
|
15
16
|
readonly maxBodyLength?: number;
|
|
17
|
+
readonly email?: EmailBackendConfig;
|
|
16
18
|
}
|
|
17
19
|
export interface RunningDevServer {
|
|
18
20
|
readonly port: number;
|
package/build/devserver/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { Server } from '@dacely/hyper-express';
|
|
4
4
|
import pc from 'picocolors';
|
|
5
5
|
import { applyCacheRule, lookupCache } from './cache.js';
|
|
6
|
+
import { initEmailService } from './email/index.js';
|
|
6
7
|
import { METHOD_CODES } from './envelope.js';
|
|
7
8
|
import { WasmServerModule } from './module.js';
|
|
8
9
|
import { proxyToVite, wireWebsocketProxy } from './proxy.js';
|
|
@@ -81,6 +82,13 @@ function sendWasmResponse(response, root, result) {
|
|
|
81
82
|
export async function startDevServer(options) {
|
|
82
83
|
const host = options.host ?? '127.0.0.1';
|
|
83
84
|
const root = path.resolve(options.root);
|
|
85
|
+
const emailInit = initEmailService(root, options.email);
|
|
86
|
+
if (emailInit.service !== null) {
|
|
87
|
+
process.stdout.write(pc.dim(` ✉ email enabled: ${emailInit.note}`) + '\n');
|
|
88
|
+
}
|
|
89
|
+
else if (emailInit.note !== null) {
|
|
90
|
+
process.stdout.write(pc.yellow(' ! ') + pc.dim(`email off: ${emailInit.note}`) + '\n');
|
|
91
|
+
}
|
|
84
92
|
const module = new WasmServerModule(options.wasmFile);
|
|
85
93
|
let warnedMissing = false;
|
|
86
94
|
let loadedOnce = false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/typescript/lib/lib.es2023.d.ts","../../node_modules/typescript/lib/lib.es2024.d.ts","../../node_modules/typescript/lib/lib.es2025.d.ts","../../node_modules/typescript/lib/lib.esnext.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.dom.asynciterable.d.ts","../../node_modules/typescript/lib/lib.webworker.d.ts","../../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../node_modules/typescript/lib/lib.webworker.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/typescript/lib/lib.es2023.array.d.ts","../../node_modules/typescript/lib/lib.es2023.collection.d.ts","../../node_modules/typescript/lib/lib.es2023.intl.d.ts","../../node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2024.collection.d.ts","../../node_modules/typescript/lib/lib.es2024.object.d.ts","../../node_modules/typescript/lib/lib.es2024.promise.d.ts","../../node_modules/typescript/lib/lib.es2024.regexp.d.ts","../../node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2024.string.d.ts","../../node_modules/typescript/lib/lib.es2025.collection.d.ts","../../node_modules/typescript/lib/lib.es2025.float16.d.ts","../../node_modules/typescript/lib/lib.es2025.intl.d.ts","../../node_modules/typescript/lib/lib.es2025.iterator.d.ts","../../node_modules/typescript/lib/lib.es2025.promise.d.ts","../../node_modules/typescript/lib/lib.es2025.regexp.d.ts","../../node_modules/typescript/lib/lib.esnext.array.d.ts","../../node_modules/typescript/lib/lib.esnext.collection.d.ts","../../node_modules/typescript/lib/lib.esnext.date.d.ts","../../node_modules/typescript/lib/lib.esnext.decorators.d.ts","../../node_modules/typescript/lib/lib.esnext.disposable.d.ts","../../node_modules/typescript/lib/lib.esnext.error.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.esnext.temporal.d.ts","../../node_modules/typescript/lib/lib.esnext.typedarrays.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../src/shared/index.ts"],"fileInfos":[{"version":"bcd24271a113971ba9eb71ff8cb01bc6b0f872a85c23fdbe5d93065b375933cd","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f88bedbeb09c6f5a6645cb24c7c55f1aa22d19ae96c8e6959cbd8b85a707bc6","impliedFormat":1},{"version":"7fe93b39b810eadd916be8db880dd7f0f7012a5cc6ffb62de8f62a2117fa6f1f","impliedFormat":1},{"version":"bb0074cc08b84a2374af33d8bf044b80851ccc9e719a5e202eacf40db2c31600","impliedFormat":1},{"version":"1a7daebe4f45fb03d9ec53d60008fbf9ac45a697fdc89e4ce218bc94b94f94d6","impliedFormat":1},{"version":"f94b133a3cb14a288803be545ac2683e0d0ff6661bcd37e31aaaec54fc382aed","impliedFormat":1},{"version":"f59d0650799f8782fd74cf73c19223730c6d1b9198671b1c5b3a38e1188b5953","impliedFormat":1},{"version":"8a15b4607d9a499e2dbeed9ec0d3c0d7372c850b2d5f1fb259e8f6d41d468a84","impliedFormat":1},{"version":"26e0fe14baee4e127f4365d1ae0b276f400562e45e19e35fd2d4c296684715e6","impliedFormat":1},{"version":"1e9332c23e9a907175e0ffc6a49e236f97b48838cc8aec9ce7e4cec21e544b65","impliedFormat":1},{"version":"3753fbc1113dc511214802a2342280a8b284ab9094f6420e7aa171e868679f91","impliedFormat":1},{"version":"999ca32883495a866aa5737fe1babc764a469e4cde6ee6b136a4b9ae68853e4b","impliedFormat":1},{"version":"17f13ecb98cbc39243f2eee1f16d45cd8ec4706b03ee314f1915f1a8b42f6984","impliedFormat":1},{"version":"d6b1eba8496bdd0eed6fc8a685768fe01b2da4a0388b5fe7df558290bffcf32f","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f57fc4404ff020bc45b9c620aff2b40f700b95fe31164024c453a5e3c163c54","impliedFormat":1},{"version":"7f57fc4404ff020bc45b9c620aff2b40f700b95fe31164024c453a5e3c163c54","impliedFormat":1},{"version":"d52ed68e7eb5881768a55713c9b5c7c443e6b46de15c04e348928b8efd20b110","affectsGlobalScope":true,"impliedFormat":1},{"version":"2a2de5b9459b3fc44decd9ce6100b72f1b002ef523126c1d3d8b2a4a63d74d78","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f57fc4404ff020bc45b9c620aff2b40f700b95fe31164024c453a5e3c163c54","impliedFormat":1},{"version":"eadcffda2aa84802c73938e589b9e58248d74c59cb7fcbca6474e3435ac15504","affectsGlobalScope":true,"impliedFormat":1},{"version":"105ba8ff7ba746404fe1a2e189d1d3d2e0eb29a08c18dded791af02f29fb4711","affectsGlobalScope":true,"impliedFormat":1},{"version":"00343ca5b2e3d48fa5df1db6e32ea2a59afab09590274a6cccb1dbae82e60c7c","affectsGlobalScope":true,"impliedFormat":1},{"version":"ebd9f816d4002697cb2864bea1f0b70a103124e18a8cd9645eeccc09bdf80ab4","affectsGlobalScope":true,"impliedFormat":1},{"version":"2c1afac30a01772cd2a9a298a7ce7706b5892e447bb46bdbeef720f7b5da77ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"7b0225f483e4fa685625ebe43dd584bb7973bbd84e66a6ba7bbe175ee1048b4f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c0a4b8ac6ce74679c1da2b3795296f5896e31c38e888469a8e0f99dc3305de60","affectsGlobalScope":true,"impliedFormat":1},{"version":"3084a7b5f569088e0146533a00830e206565de65cae2239509168b11434cd84f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5079c53f0f141a0698faa903e76cb41cd664e3efb01cc17a5c46ec2eb0bef42","affectsGlobalScope":true,"impliedFormat":1},{"version":"32cafbc484dea6b0ab62cf8473182bbcb23020d70845b406f80b7526f38ae862","affectsGlobalScope":true,"impliedFormat":1},{"version":"fca4cdcb6d6c5ef18a869003d02c9f0fd95df8cfaf6eb431cd3376bc034cad36","affectsGlobalScope":true,"impliedFormat":1},{"version":"b93ec88115de9a9dc1b602291b85baf825c85666bf25985cc5f698073892b467","affectsGlobalScope":true,"impliedFormat":1},{"version":"f5c06dcc3fe849fcb297c247865a161f995cc29de7aa823afdd75aaaddc1419b","affectsGlobalScope":true,"impliedFormat":1},{"version":"b77e16112127a4b169ef0b8c3a4d730edf459c5f25fe52d5e436a6919206c4d7","affectsGlobalScope":true,"impliedFormat":1},{"version":"fbffd9337146eff822c7c00acbb78b01ea7ea23987f6c961eba689349e744f8c","affectsGlobalScope":true,"impliedFormat":1},{"version":"a995c0e49b721312f74fdfb89e4ba29bd9824c770bbb4021d74d2bf560e4c6bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"c7b3542146734342e440a84b213384bfa188835537ddbda50d30766f0593aff9","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce6180fa19b1cccd07ee7f7dbb9a367ac19c0ed160573e4686425060b6df7f57","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f02e2476bccb9dbe21280d6090f0df17d2f66b74711489415a8aa4df73c9675","affectsGlobalScope":true,"impliedFormat":1},{"version":"45e3ab34c1c013c8ab2dc1ba4c80c780744b13b5676800ae2e3be27ae862c40c","affectsGlobalScope":true,"impliedFormat":1},{"version":"805c86f6cca8d7702a62a844856dbaa2a3fd2abef0536e65d48732441dde5b5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e42e397f1a5a77994f0185fd1466520691456c772d06bf843e5084ceb879a0ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"f4c2b41f90c95b1c532ecc874bd3c111865793b23aebcc1c3cbbabcd5d76ffb0","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab26191cfad5b66afa11b8bf935ef1cd88fabfcb28d30b2dfa6fad877d050332","affectsGlobalScope":true,"impliedFormat":1},{"version":"2088bc26531e38fb05eedac2951480db5309f6be3fa4a08d2221abb0f5b4200d","affectsGlobalScope":true,"impliedFormat":1},{"version":"cb9d366c425fea79716a8fb3af0d78e6b22ebbab3bd64d25063b42dc9f531c1e","affectsGlobalScope":true,"impliedFormat":1},{"version":"500934a8089c26d57ebdb688fc9757389bb6207a3c8f0674d68efa900d2abb34","affectsGlobalScope":true,"impliedFormat":1},{"version":"689da16f46e647cef0d64b0def88910e818a5877ca5379ede156ca3afb780ac3","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc21cc8b6fee4f4c2440d08035b7ea3c06b3511314c8bab6bef7a92de58a2593","affectsGlobalScope":true,"impliedFormat":1},{"version":"7ca53d13d2957003abb47922a71866ba7cb2068f8d154877c596d63c359fed25","affectsGlobalScope":true,"impliedFormat":1},{"version":"54725f8c4df3d900cb4dac84b64689ce29548da0b4e9b7c2de61d41c79293611","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5594bc3076ac29e6c1ebda77939bc4c8833de72f654b6e376862c0473199323","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f3eb332c2d73e729f3364fcc0c2b375e72a121e8157d25a82d67a138c83a95c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6f4427f9642ce8d500970e4e69d1397f64072ab73b97e476b4002a646ac743b1","affectsGlobalScope":true,"impliedFormat":1},{"version":"48915f327cd1dea4d7bd358d9dc7732f58f9e1626a29cc0c05c8c692419d9bb7","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7bf9377723203b5a6a4b920164df22d56a43f593269ba6ae1fdc97774b68855","affectsGlobalScope":true,"impliedFormat":1},{"version":"db9709688f82c9e5f65a119c64d835f906efe5f559d08b11642d56eb85b79357","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b25b8c874acd1a4cf8444c3617e037d444d19080ac9f634b405583fd10ce1f7","affectsGlobalScope":true,"impliedFormat":1},{"version":"37be57d7c90cf1f8112ee2636a068d8fd181289f82b744160ec56a7dc158a9f5","affectsGlobalScope":true,"impliedFormat":1},{"version":"a917a49ac94cd26b754ab84e113369a75d1a47a710661d7cd25e961cc797065f","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d3261badeb7843d157ef3e6f5d1427d0eeb0af0cf9df84a62cfd29fd47ac86e","affectsGlobalScope":true,"impliedFormat":1},{"version":"195daca651dde22f2167ac0d0a05e215308119a3100f5e6268e8317d05a92526","affectsGlobalScope":true,"impliedFormat":1},{"version":"8b11e4285cd2bb164a4dc09248bdec69e9842517db4ca47c1ba913011e44ff2f","affectsGlobalScope":true,"impliedFormat":1},{"version":"0508571a52475e245b02bc50fa1394065a0a3d05277fbf5120c3784b85651799","affectsGlobalScope":true,"impliedFormat":1},{"version":"8f9af488f510c3015af3cc8c267a9e9d96c4dd38a1fdff0e11dc5a544711415b","affectsGlobalScope":true,"impliedFormat":1},{"version":"fc611fea8d30ea72c6bbfb599c9b4d393ce22e2f5bfef2172534781e7d138104","affectsGlobalScope":true,"impliedFormat":1},{"version":"0bd714129fca875f7d4c477a1a392200b0bcd13fb2e80928cd334b63830ea047","affectsGlobalScope":true,"impliedFormat":1},{"version":"e2c9037ae6cd2c52d80ceef0b3c5ffdb488627d71529cf4f63776daf11161c9a","affectsGlobalScope":true,"impliedFormat":1},{"version":"135d5cf4d345f59f1a9caadfafcd858d3d9cc68290db616cc85797224448cccc","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc238c3f81c2984751932b6aab223cd5b830e0ac6cad76389e5e9d2ffc03287d","affectsGlobalScope":true,"impliedFormat":1},{"version":"4a07f9b76d361f572620927e5735b77d6d2101c23cdd94383eb5b706e7b36357","affectsGlobalScope":true,"impliedFormat":1},{"version":"7c4e8dc6ab834cc6baa0227e030606d29e3e8449a9f67cdf5605ea5493c4db29","affectsGlobalScope":true,"impliedFormat":1},{"version":"de7ba0fd02e06cd9a5bd4ab441ed0e122735786e67dde1e849cced1cd8b46b78","affectsGlobalScope":true,"impliedFormat":1},{"version":"6148e4e88d720a06855071c3db02069434142a8332cf9c182cda551adedf3156","affectsGlobalScope":true,"impliedFormat":1},{"version":"d63dba625b108316a40c95a4425f8d4294e0deeccfd6c7e59d819efa19e23409","affectsGlobalScope":true,"impliedFormat":1},{"version":"0568d6befee03dd435bed4fc25c4e46865b24bdcb8c563fdc21f580a2c301904","affectsGlobalScope":true,"impliedFormat":1},{"version":"30d62269b05b584741f19a5369852d5d34895aa2ac4fd948956f886d15f9cc0d","affectsGlobalScope":true,"impliedFormat":1},{"version":"f128dae7c44d8f35ee42e0a437000a57c9f06cc04f8b4fb42eebf44954d53dc8","affectsGlobalScope":true,"impliedFormat":1},{"version":"ffbe6d7b295306b2ba88030f65b74c107d8d99bdcf596ea99c62a02f606108b0","affectsGlobalScope":true,"impliedFormat":1},{"version":"996fb27b15277369c68a4ba46ed138b4e9e839a02fb4ec756f7997629242fd9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"79b712591b270d4778c89706ca2cfc56ddb8c3f895840e477388f1710dc5eda9","affectsGlobalScope":true,"impliedFormat":1},{"version":"20884846cef428b992b9bd032e70a4ef88e349263f63aeddf04dda837a7dba26","affectsGlobalScope":true,"impliedFormat":1},{"version":"5fcab789c73a97cd43828ee3cc94a61264cf24d4c44472ce64ced0e0f148bdb2","affectsGlobalScope":true,"impliedFormat":1},{"version":"db59a81f070c1880ad645b2c0275022baa6a0c4f0acdc58d29d349c6efcf0903","affectsGlobalScope":true,"impliedFormat":1},{"version":"673294292640f5722b700e7d814e17aaf7d93f83a48a2c9b38f33cbc940ad8b0","affectsGlobalScope":true,"impliedFormat":1},{"version":"d786b48f934cbca483b3c6d0a798cb43bbb4ada283e76fb22c28e53ae05b9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ecb8e347cb6b2a8927c09b86263663289418df375f5e68e11a0ae683776978f","affectsGlobalScope":true,"impliedFormat":1},{"version":"142efd4ce210576f777dc34df121777be89eda476942d6d6663b03dcb53be3ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"379bc41580c2d774f82e828c70308f24a005b490c25ba34d679d84bcf05c3d9d","affectsGlobalScope":true,"impliedFormat":1},{"version":"ed484fb2aa8a1a23d0277056ec3336e0a0b52f9b8d6a961f338a642faf43235d","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ffedae1d1c2d53fdbca1c96d3c7dda544281f7d262f99b6880634f8fd8d9820","affectsGlobalScope":true,"impliedFormat":1},{"version":"83a730b125d477dd264df8ba479afab27a3dae7152b005c214ab94dc7ee44fd3","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ce14b81c5cc821994aa8ec1d42b220dd41b27fcc06373bce3958af7421b77d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3a048b3e9302ef9a34ef4ebb9aecfb28b66abb3bce577206a79fee559c230da","affectsGlobalScope":true,"impliedFormat":1},{"version":"cd1ab684b7f857a481df0565fce2813d4356e56d5bd783f7662ecb49463defd7","signature":"bc0e7739174346f312888f4d11ec9a2dc8494ace48e00b14cb1101b406407c28"}],"root":[94],"options":{"allowJs":true,"allowSyntheticDefaultImports":true,"alwaysStrict":true,"declaration":true,"esModuleInterop":true,"experimentalDecorators":true,"module":99,"noImplicitAny":true,"outDir":"./","preserveConstEnums":true,"removeComments":true,"rootDir":"../../src/shared","skipLibCheck":true,"sourceMap":false,"strict":true,"strictBindCallApply":true,"strictFunctionTypes":true,"strictNullChecks":true,"strictPropertyInitialization":true,"suppressImplicitAnyIndexErrors":false,"target":99,"tsBuildInfoFile":"./.tsbuildinfo"},"version":"6.0.3"}
|
|
1
|
+
{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/typescript/lib/lib.es2023.d.ts","../../node_modules/typescript/lib/lib.es2024.d.ts","../../node_modules/typescript/lib/lib.es2025.d.ts","../../node_modules/typescript/lib/lib.esnext.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.dom.asynciterable.d.ts","../../node_modules/typescript/lib/lib.webworker.d.ts","../../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../node_modules/typescript/lib/lib.webworker.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/typescript/lib/lib.es2023.array.d.ts","../../node_modules/typescript/lib/lib.es2023.collection.d.ts","../../node_modules/typescript/lib/lib.es2023.intl.d.ts","../../node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2024.collection.d.ts","../../node_modules/typescript/lib/lib.es2024.object.d.ts","../../node_modules/typescript/lib/lib.es2024.promise.d.ts","../../node_modules/typescript/lib/lib.es2024.regexp.d.ts","../../node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2024.string.d.ts","../../node_modules/typescript/lib/lib.es2025.collection.d.ts","../../node_modules/typescript/lib/lib.es2025.float16.d.ts","../../node_modules/typescript/lib/lib.es2025.intl.d.ts","../../node_modules/typescript/lib/lib.es2025.iterator.d.ts","../../node_modules/typescript/lib/lib.es2025.promise.d.ts","../../node_modules/typescript/lib/lib.es2025.regexp.d.ts","../../node_modules/typescript/lib/lib.esnext.array.d.ts","../../node_modules/typescript/lib/lib.esnext.collection.d.ts","../../node_modules/typescript/lib/lib.esnext.date.d.ts","../../node_modules/typescript/lib/lib.esnext.decorators.d.ts","../../node_modules/typescript/lib/lib.esnext.disposable.d.ts","../../node_modules/typescript/lib/lib.esnext.error.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.esnext.temporal.d.ts","../../node_modules/typescript/lib/lib.esnext.typedarrays.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../src/shared/index.ts"],"fileInfos":[{"version":"bcd24271a113971ba9eb71ff8cb01bc6b0f872a85c23fdbe5d93065b375933cd","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f88bedbeb09c6f5a6645cb24c7c55f1aa22d19ae96c8e6959cbd8b85a707bc6","impliedFormat":1},{"version":"7fe93b39b810eadd916be8db880dd7f0f7012a5cc6ffb62de8f62a2117fa6f1f","impliedFormat":1},{"version":"bb0074cc08b84a2374af33d8bf044b80851ccc9e719a5e202eacf40db2c31600","impliedFormat":1},{"version":"1a7daebe4f45fb03d9ec53d60008fbf9ac45a697fdc89e4ce218bc94b94f94d6","impliedFormat":1},{"version":"f94b133a3cb14a288803be545ac2683e0d0ff6661bcd37e31aaaec54fc382aed","impliedFormat":1},{"version":"f59d0650799f8782fd74cf73c19223730c6d1b9198671b1c5b3a38e1188b5953","impliedFormat":1},{"version":"8a15b4607d9a499e2dbeed9ec0d3c0d7372c850b2d5f1fb259e8f6d41d468a84","impliedFormat":1},{"version":"26e0fe14baee4e127f4365d1ae0b276f400562e45e19e35fd2d4c296684715e6","impliedFormat":1},{"version":"1e9332c23e9a907175e0ffc6a49e236f97b48838cc8aec9ce7e4cec21e544b65","impliedFormat":1},{"version":"3753fbc1113dc511214802a2342280a8b284ab9094f6420e7aa171e868679f91","impliedFormat":1},{"version":"999ca32883495a866aa5737fe1babc764a469e4cde6ee6b136a4b9ae68853e4b","impliedFormat":1},{"version":"17f13ecb98cbc39243f2eee1f16d45cd8ec4706b03ee314f1915f1a8b42f6984","impliedFormat":1},{"version":"d6b1eba8496bdd0eed6fc8a685768fe01b2da4a0388b5fe7df558290bffcf32f","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f57fc4404ff020bc45b9c620aff2b40f700b95fe31164024c453a5e3c163c54","impliedFormat":1},{"version":"7f57fc4404ff020bc45b9c620aff2b40f700b95fe31164024c453a5e3c163c54","impliedFormat":1},{"version":"d52ed68e7eb5881768a55713c9b5c7c443e6b46de15c04e348928b8efd20b110","affectsGlobalScope":true,"impliedFormat":1},{"version":"2a2de5b9459b3fc44decd9ce6100b72f1b002ef523126c1d3d8b2a4a63d74d78","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f57fc4404ff020bc45b9c620aff2b40f700b95fe31164024c453a5e3c163c54","impliedFormat":1},{"version":"eadcffda2aa84802c73938e589b9e58248d74c59cb7fcbca6474e3435ac15504","affectsGlobalScope":true,"impliedFormat":1},{"version":"105ba8ff7ba746404fe1a2e189d1d3d2e0eb29a08c18dded791af02f29fb4711","affectsGlobalScope":true,"impliedFormat":1},{"version":"00343ca5b2e3d48fa5df1db6e32ea2a59afab09590274a6cccb1dbae82e60c7c","affectsGlobalScope":true,"impliedFormat":1},{"version":"ebd9f816d4002697cb2864bea1f0b70a103124e18a8cd9645eeccc09bdf80ab4","affectsGlobalScope":true,"impliedFormat":1},{"version":"2c1afac30a01772cd2a9a298a7ce7706b5892e447bb46bdbeef720f7b5da77ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"7b0225f483e4fa685625ebe43dd584bb7973bbd84e66a6ba7bbe175ee1048b4f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c0a4b8ac6ce74679c1da2b3795296f5896e31c38e888469a8e0f99dc3305de60","affectsGlobalScope":true,"impliedFormat":1},{"version":"3084a7b5f569088e0146533a00830e206565de65cae2239509168b11434cd84f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5079c53f0f141a0698faa903e76cb41cd664e3efb01cc17a5c46ec2eb0bef42","affectsGlobalScope":true,"impliedFormat":1},{"version":"32cafbc484dea6b0ab62cf8473182bbcb23020d70845b406f80b7526f38ae862","affectsGlobalScope":true,"impliedFormat":1},{"version":"fca4cdcb6d6c5ef18a869003d02c9f0fd95df8cfaf6eb431cd3376bc034cad36","affectsGlobalScope":true,"impliedFormat":1},{"version":"b93ec88115de9a9dc1b602291b85baf825c85666bf25985cc5f698073892b467","affectsGlobalScope":true,"impliedFormat":1},{"version":"f5c06dcc3fe849fcb297c247865a161f995cc29de7aa823afdd75aaaddc1419b","affectsGlobalScope":true,"impliedFormat":1},{"version":"b77e16112127a4b169ef0b8c3a4d730edf459c5f25fe52d5e436a6919206c4d7","affectsGlobalScope":true,"impliedFormat":1},{"version":"fbffd9337146eff822c7c00acbb78b01ea7ea23987f6c961eba689349e744f8c","affectsGlobalScope":true,"impliedFormat":1},{"version":"a995c0e49b721312f74fdfb89e4ba29bd9824c770bbb4021d74d2bf560e4c6bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"c7b3542146734342e440a84b213384bfa188835537ddbda50d30766f0593aff9","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce6180fa19b1cccd07ee7f7dbb9a367ac19c0ed160573e4686425060b6df7f57","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f02e2476bccb9dbe21280d6090f0df17d2f66b74711489415a8aa4df73c9675","affectsGlobalScope":true,"impliedFormat":1},{"version":"45e3ab34c1c013c8ab2dc1ba4c80c780744b13b5676800ae2e3be27ae862c40c","affectsGlobalScope":true,"impliedFormat":1},{"version":"805c86f6cca8d7702a62a844856dbaa2a3fd2abef0536e65d48732441dde5b5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e42e397f1a5a77994f0185fd1466520691456c772d06bf843e5084ceb879a0ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"f4c2b41f90c95b1c532ecc874bd3c111865793b23aebcc1c3cbbabcd5d76ffb0","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab26191cfad5b66afa11b8bf935ef1cd88fabfcb28d30b2dfa6fad877d050332","affectsGlobalScope":true,"impliedFormat":1},{"version":"2088bc26531e38fb05eedac2951480db5309f6be3fa4a08d2221abb0f5b4200d","affectsGlobalScope":true,"impliedFormat":1},{"version":"cb9d366c425fea79716a8fb3af0d78e6b22ebbab3bd64d25063b42dc9f531c1e","affectsGlobalScope":true,"impliedFormat":1},{"version":"500934a8089c26d57ebdb688fc9757389bb6207a3c8f0674d68efa900d2abb34","affectsGlobalScope":true,"impliedFormat":1},{"version":"689da16f46e647cef0d64b0def88910e818a5877ca5379ede156ca3afb780ac3","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc21cc8b6fee4f4c2440d08035b7ea3c06b3511314c8bab6bef7a92de58a2593","affectsGlobalScope":true,"impliedFormat":1},{"version":"7ca53d13d2957003abb47922a71866ba7cb2068f8d154877c596d63c359fed25","affectsGlobalScope":true,"impliedFormat":1},{"version":"54725f8c4df3d900cb4dac84b64689ce29548da0b4e9b7c2de61d41c79293611","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5594bc3076ac29e6c1ebda77939bc4c8833de72f654b6e376862c0473199323","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f3eb332c2d73e729f3364fcc0c2b375e72a121e8157d25a82d67a138c83a95c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6f4427f9642ce8d500970e4e69d1397f64072ab73b97e476b4002a646ac743b1","affectsGlobalScope":true,"impliedFormat":1},{"version":"48915f327cd1dea4d7bd358d9dc7732f58f9e1626a29cc0c05c8c692419d9bb7","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7bf9377723203b5a6a4b920164df22d56a43f593269ba6ae1fdc97774b68855","affectsGlobalScope":true,"impliedFormat":1},{"version":"db9709688f82c9e5f65a119c64d835f906efe5f559d08b11642d56eb85b79357","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b25b8c874acd1a4cf8444c3617e037d444d19080ac9f634b405583fd10ce1f7","affectsGlobalScope":true,"impliedFormat":1},{"version":"37be57d7c90cf1f8112ee2636a068d8fd181289f82b744160ec56a7dc158a9f5","affectsGlobalScope":true,"impliedFormat":1},{"version":"a917a49ac94cd26b754ab84e113369a75d1a47a710661d7cd25e961cc797065f","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d3261badeb7843d157ef3e6f5d1427d0eeb0af0cf9df84a62cfd29fd47ac86e","affectsGlobalScope":true,"impliedFormat":1},{"version":"195daca651dde22f2167ac0d0a05e215308119a3100f5e6268e8317d05a92526","affectsGlobalScope":true,"impliedFormat":1},{"version":"8b11e4285cd2bb164a4dc09248bdec69e9842517db4ca47c1ba913011e44ff2f","affectsGlobalScope":true,"impliedFormat":1},{"version":"0508571a52475e245b02bc50fa1394065a0a3d05277fbf5120c3784b85651799","affectsGlobalScope":true,"impliedFormat":1},{"version":"8f9af488f510c3015af3cc8c267a9e9d96c4dd38a1fdff0e11dc5a544711415b","affectsGlobalScope":true,"impliedFormat":1},{"version":"fc611fea8d30ea72c6bbfb599c9b4d393ce22e2f5bfef2172534781e7d138104","affectsGlobalScope":true,"impliedFormat":1},{"version":"0bd714129fca875f7d4c477a1a392200b0bcd13fb2e80928cd334b63830ea047","affectsGlobalScope":true,"impliedFormat":1},{"version":"e2c9037ae6cd2c52d80ceef0b3c5ffdb488627d71529cf4f63776daf11161c9a","affectsGlobalScope":true,"impliedFormat":1},{"version":"135d5cf4d345f59f1a9caadfafcd858d3d9cc68290db616cc85797224448cccc","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc238c3f81c2984751932b6aab223cd5b830e0ac6cad76389e5e9d2ffc03287d","affectsGlobalScope":true,"impliedFormat":1},{"version":"4a07f9b76d361f572620927e5735b77d6d2101c23cdd94383eb5b706e7b36357","affectsGlobalScope":true,"impliedFormat":1},{"version":"7c4e8dc6ab834cc6baa0227e030606d29e3e8449a9f67cdf5605ea5493c4db29","affectsGlobalScope":true,"impliedFormat":1},{"version":"de7ba0fd02e06cd9a5bd4ab441ed0e122735786e67dde1e849cced1cd8b46b78","affectsGlobalScope":true,"impliedFormat":1},{"version":"6148e4e88d720a06855071c3db02069434142a8332cf9c182cda551adedf3156","affectsGlobalScope":true,"impliedFormat":1},{"version":"d63dba625b108316a40c95a4425f8d4294e0deeccfd6c7e59d819efa19e23409","affectsGlobalScope":true,"impliedFormat":1},{"version":"0568d6befee03dd435bed4fc25c4e46865b24bdcb8c563fdc21f580a2c301904","affectsGlobalScope":true,"impliedFormat":1},{"version":"30d62269b05b584741f19a5369852d5d34895aa2ac4fd948956f886d15f9cc0d","affectsGlobalScope":true,"impliedFormat":1},{"version":"f128dae7c44d8f35ee42e0a437000a57c9f06cc04f8b4fb42eebf44954d53dc8","affectsGlobalScope":true,"impliedFormat":1},{"version":"ffbe6d7b295306b2ba88030f65b74c107d8d99bdcf596ea99c62a02f606108b0","affectsGlobalScope":true,"impliedFormat":1},{"version":"996fb27b15277369c68a4ba46ed138b4e9e839a02fb4ec756f7997629242fd9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"79b712591b270d4778c89706ca2cfc56ddb8c3f895840e477388f1710dc5eda9","affectsGlobalScope":true,"impliedFormat":1},{"version":"20884846cef428b992b9bd032e70a4ef88e349263f63aeddf04dda837a7dba26","affectsGlobalScope":true,"impliedFormat":1},{"version":"5fcab789c73a97cd43828ee3cc94a61264cf24d4c44472ce64ced0e0f148bdb2","affectsGlobalScope":true,"impliedFormat":1},{"version":"db59a81f070c1880ad645b2c0275022baa6a0c4f0acdc58d29d349c6efcf0903","affectsGlobalScope":true,"impliedFormat":1},{"version":"673294292640f5722b700e7d814e17aaf7d93f83a48a2c9b38f33cbc940ad8b0","affectsGlobalScope":true,"impliedFormat":1},{"version":"d786b48f934cbca483b3c6d0a798cb43bbb4ada283e76fb22c28e53ae05b9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ecb8e347cb6b2a8927c09b86263663289418df375f5e68e11a0ae683776978f","affectsGlobalScope":true,"impliedFormat":1},{"version":"142efd4ce210576f777dc34df121777be89eda476942d6d6663b03dcb53be3ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"379bc41580c2d774f82e828c70308f24a005b490c25ba34d679d84bcf05c3d9d","affectsGlobalScope":true,"impliedFormat":1},{"version":"ed484fb2aa8a1a23d0277056ec3336e0a0b52f9b8d6a961f338a642faf43235d","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ffedae1d1c2d53fdbca1c96d3c7dda544281f7d262f99b6880634f8fd8d9820","affectsGlobalScope":true,"impliedFormat":1},{"version":"83a730b125d477dd264df8ba479afab27a3dae7152b005c214ab94dc7ee44fd3","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ce14b81c5cc821994aa8ec1d42b220dd41b27fcc06373bce3958af7421b77d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3a048b3e9302ef9a34ef4ebb9aecfb28b66abb3bce577206a79fee559c230da","affectsGlobalScope":true,"impliedFormat":1},{"version":"5da671004554752a1e26662fc5be318c9fb6c795ab26abb25f66ea40de484220","signature":"7427c56ad284f0a61481dac8b11e66b3282aac3674ed9c7bcd91760566157867"}],"root":[94],"options":{"allowJs":true,"allowSyntheticDefaultImports":true,"alwaysStrict":true,"declaration":true,"esModuleInterop":true,"experimentalDecorators":true,"module":99,"noImplicitAny":true,"outDir":"./","preserveConstEnums":true,"removeComments":true,"rootDir":"../../src/shared","skipLibCheck":true,"sourceMap":false,"strict":true,"strictBindCallApply":true,"strictFunctionTypes":true,"strictNullChecks":true,"strictPropertyInitialization":true,"suppressImplicitAnyIndexErrors":false,"target":99,"tsBuildInfoFile":"./.tsbuildinfo"},"version":"6.0.3"}
|
package/build/shared/index.d.ts
CHANGED
|
@@ -2,3 +2,16 @@ export declare const FRAMEWORK_NAME = "toiljs";
|
|
|
2
2
|
export interface ToilTarget {
|
|
3
3
|
readonly name: 'client' | 'compiler' | 'cli' | 'server';
|
|
4
4
|
}
|
|
5
|
+
export interface SmtpBackendConfig {
|
|
6
|
+
readonly host?: string;
|
|
7
|
+
readonly port?: number;
|
|
8
|
+
readonly user?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface EmailBackendConfig {
|
|
11
|
+
readonly provider?: 'resend' | 'gmail' | 'smtp';
|
|
12
|
+
readonly from?: string;
|
|
13
|
+
readonly maxPerMin?: number;
|
|
14
|
+
readonly maxPerDay?: number;
|
|
15
|
+
readonly maxPerRecipientPerHour?: number;
|
|
16
|
+
readonly smtp?: SmtpBackendConfig;
|
|
17
|
+
}
|
package/docs/email.md
CHANGED
|
@@ -95,9 +95,36 @@ TOIL_EMAIL_SMTP_USER=noreply@example.com
|
|
|
95
95
|
|
|
96
96
|
### In dev
|
|
97
97
|
|
|
98
|
-
`toiljs dev`
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
`toiljs dev` runs the **full email pipeline** in Node — recipient validation,
|
|
99
|
+
dedup, and the per-minute / per-day / per-recipient caps all behave exactly like
|
|
100
|
+
the edge — and **really sends** once you configure a provider. Configure it in
|
|
101
|
+
`toil.config.ts` (non-secret) with the API key in `.env.secrets`:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
// toil.config.ts
|
|
105
|
+
import { defineConfig } from 'toiljs/compiler';
|
|
106
|
+
export default defineConfig({
|
|
107
|
+
server: {
|
|
108
|
+
email: { provider: 'resend', from: 'you@example.com', maxPerMin: 60 },
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# .env.secrets (gitignored)
|
|
115
|
+
TOIL_EMAIL_API_KEY=re_xxxxxxxxxxxx
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`TOIL_EMAIL_*` env vars override the config file (so the same `.env.secrets` the
|
|
119
|
+
edge uses works in dev too). Supports `resend` and `gmail`/`smtp` (SMTP via
|
|
120
|
+
nodemailer). **Not configured?** `EmailService.send` stays a log-only mock and
|
|
121
|
+
returns `Sent`, so a flow that sends email still works without setup.
|
|
122
|
+
|
|
123
|
+
> Because the dev server runs the guest **synchronously**, the actual network
|
|
124
|
+
> send is fire-and-forget: validation + caps return their exact status
|
|
125
|
+
> immediately, but a `Sent` is optimistic and the real delivery outcome (or
|
|
126
|
+
> `ProviderError`) is logged, not returned. The ABI is identical to the edge, so
|
|
127
|
+
> code that runs in dev runs on the edge.
|
|
101
128
|
|
|
102
129
|
## Sending email
|
|
103
130
|
|
|
@@ -111,8 +138,8 @@ class Notify {
|
|
|
111
138
|
const status = EmailService.send(
|
|
112
139
|
'alice@example.com',
|
|
113
140
|
'Welcome!',
|
|
114
|
-
'Thanks for signing up.',
|
|
115
|
-
'welcome',
|
|
141
|
+
'Thanks for signing up.', // plain-text body
|
|
142
|
+
'welcome', // purpose tag (dedup / abuse keying)
|
|
116
143
|
'<h1>Thanks for signing up.</h1>', // optional HTML body
|
|
117
144
|
);
|
|
118
145
|
return status == EmailStatus.Sent
|
|
@@ -124,16 +151,16 @@ class Notify {
|
|
|
124
151
|
|
|
125
152
|
`send(to, subject, body, purpose = 'tx', html = '')` returns an **`EmailStatus`**:
|
|
126
153
|
|
|
127
|
-
| Status
|
|
128
|
-
|
|
|
129
|
-
| `Sent`
|
|
130
|
-
| `Deduped`
|
|
131
|
-
| `Budget`
|
|
132
|
-
| `TryLater`
|
|
133
|
-
| `RecipientCapped` | The per-recipient hourly cap was hit
|
|
134
|
-
| `BadRecipient`
|
|
135
|
-
| `Disabled`
|
|
136
|
-
| `ProviderError`
|
|
154
|
+
| Status | Meaning | Retry? |
|
|
155
|
+
| ----------------- | ----------------------------------------------------------- | ---------------- |
|
|
156
|
+
| `Sent` | Accepted by the provider | — |
|
|
157
|
+
| `Deduped` | An identical recent `(recipient, purpose)` was collapsed | treat as sent |
|
|
158
|
+
| `Budget` | The host's per-minute budget is exhausted | yes, later |
|
|
159
|
+
| `TryLater` | The mailer was saturated / a queue was full | yes, back off |
|
|
160
|
+
| `RecipientCapped` | The per-recipient hourly cap was hit | no (this window) |
|
|
161
|
+
| `BadRecipient` | The address failed validation (CRLF, multiple addresses) | no |
|
|
162
|
+
| `Disabled` | This host has no `[email]` capability | no |
|
|
163
|
+
| `ProviderError` | The provider rejected it, or transport failed after retries | no |
|
|
137
164
|
|
|
138
165
|
`purpose` is a short, non-PII tag (`"welcome"`, `"reset"`, …). The mailer folds
|
|
139
166
|
it into the **dedup** key (identical `(host, recipient, purpose)` within ~30s is
|
|
@@ -149,8 +176,8 @@ when the same email is sent with different values:
|
|
|
149
176
|
|
|
150
177
|
```ts
|
|
151
178
|
const welcome = new EmailTemplate(
|
|
152
|
-
'Welcome, {{name}}!',
|
|
153
|
-
'Hi {{name}}, your code is {{code}}.',
|
|
179
|
+
'Welcome, {{name}}!', // subject
|
|
180
|
+
'Hi {{name}}, your code is {{code}}.', // plain-text body
|
|
154
181
|
'<h1>Welcome, {{name}}</h1><p>Code: <b>{{code}}</b></p>', // html (optional)
|
|
155
182
|
);
|
|
156
183
|
|
|
@@ -182,12 +209,16 @@ export const subject = 'Welcome, {{name}}!';
|
|
|
182
209
|
|
|
183
210
|
export default function Welcome({ name, code }: { name: string; code: string }) {
|
|
184
211
|
return (
|
|
185
|
-
<table
|
|
212
|
+
<table
|
|
213
|
+
width="100%"
|
|
214
|
+
style={{ fontFamily: 'Arial, sans-serif' }}>
|
|
186
215
|
<tbody>
|
|
187
216
|
<tr>
|
|
188
217
|
<td style={{ padding: '24px' }}>
|
|
189
218
|
<h1 style={{ color: '#111' }}>Welcome, {name}!</h1>
|
|
190
|
-
<p>
|
|
219
|
+
<p>
|
|
220
|
+
Your code is <b>{code}</b>.
|
|
221
|
+
</p>
|
|
191
222
|
</td>
|
|
192
223
|
</tr>
|
|
193
224
|
</tbody>
|
|
@@ -206,9 +237,12 @@ const status = Emails.Welcome.send('alice@example.com', '123456', 'Alice');
|
|
|
206
237
|
|
|
207
238
|
Authoring notes:
|
|
208
239
|
|
|
209
|
-
- **
|
|
210
|
-
inline
|
|
211
|
-
inlined for you at build (
|
|
240
|
+
- **Styles must end up inline.** Email clients strip `<style>`/external CSS, so
|
|
241
|
+
write inline `style={{ ... }}`, or import a stylesheet and its rules are
|
|
242
|
+
inlined into element `style="…"` for you at build (a bare CSS import has no
|
|
243
|
+
effect on its own under SSR). Keep email-only styles next to the email, e.g.
|
|
244
|
+
`import './styles/email.css'`, or **reuse existing project CSS** with `import
|
|
245
|
+
'client/styles/…'` (the `client/*` alias points at your client source).
|
|
212
246
|
- **Optional exports:** `export const subject` (a token template; defaults to the
|
|
213
247
|
email name), `export const text` (a plain-text alternative; otherwise derived
|
|
214
248
|
from the HTML), `export const purpose`.
|
|
@@ -220,6 +254,14 @@ Authoring notes:
|
|
|
220
254
|
- The generated `server/_emails.ts` is regenerated on `build`/`dev` and should be
|
|
221
255
|
gitignored.
|
|
222
256
|
|
|
257
|
+
### Preview while you author
|
|
258
|
+
|
|
259
|
+
While `toiljs dev` runs, open **`/__toil/emails`** (the dev banner prints the
|
|
260
|
+
link). It lists every `emails/*.tsx`, renders the selected one exactly as the
|
|
261
|
+
build does (imported `client/*` CSS inlined), lets you fill each `{{token}}` to
|
|
262
|
+
see the result, toggle the HTML and plain-text parts, and open the file in your
|
|
263
|
+
editor. It refreshes live as you edit the template or its CSS.
|
|
264
|
+
|
|
223
265
|
## Email verification codes (`TwoFactor`)
|
|
224
266
|
|
|
225
267
|
`TwoFactor` is a **stateless** email-code primitive (2FA, email confirmation,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toiljs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.46",
|
|
5
5
|
"author": "Dacely",
|
|
6
6
|
"description": "The modern React framework: a file-based React frontend and a ToilScript-compiled WebAssembly backend.",
|
|
7
7
|
"repository": {
|
|
@@ -127,6 +127,7 @@
|
|
|
127
127
|
"eslint-plugin-react-refresh": "^0.5.2",
|
|
128
128
|
"hash-wasm": "^4.12.0",
|
|
129
129
|
"juice": "^12.1.0",
|
|
130
|
+
"nodemailer": "^9.0.0",
|
|
130
131
|
"picocolors": "^1.1.1",
|
|
131
132
|
"sharp": "^0.35.0",
|
|
132
133
|
"toilscript": "^0.1.25",
|
|
@@ -152,10 +153,11 @@
|
|
|
152
153
|
"@btc-vision/as-pect-cli": "^8.3.0",
|
|
153
154
|
"@btc-vision/as-pect-transform": "^8.3.0",
|
|
154
155
|
"@clack/prompts": "^1.5.0",
|
|
155
|
-
"@microsoft/api-extractor": "7.58.
|
|
156
|
+
"@microsoft/api-extractor": "7.58.9",
|
|
156
157
|
"@testing-library/dom": "^10.4.1",
|
|
157
158
|
"@testing-library/react": "^16.3.2",
|
|
158
159
|
"@types/node": "^25.9.1",
|
|
160
|
+
"@types/nodemailer": "^8.0.1",
|
|
159
161
|
"@types/react": "^19.2.15",
|
|
160
162
|
"@types/react-dom": "^19.2.3",
|
|
161
163
|
"@vitest/coverage-v8": "^4.1.7",
|