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.
Files changed (62) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/RSG.md +105 -27
  3. package/build/cli/.tsbuildinfo +1 -1
  4. package/build/cli/index.js +12 -2
  5. package/build/compiler/.tsbuildinfo +1 -1
  6. package/build/compiler/config.d.ts +4 -0
  7. package/build/compiler/config.js +1 -0
  8. package/build/compiler/email-preview.d.ts +12 -0
  9. package/build/compiler/email-preview.js +253 -0
  10. package/build/compiler/emails.d.ts +6 -3
  11. package/build/compiler/emails.js +52 -12
  12. package/build/compiler/index.js +15 -0
  13. package/build/compiler/plugin.js +64 -2
  14. package/build/compiler/vite.js +1 -0
  15. package/build/devserver/.tsbuildinfo +1 -1
  16. package/build/devserver/dotenv.d.ts +8 -0
  17. package/build/devserver/dotenv.js +59 -0
  18. package/build/devserver/email/caps.d.ts +9 -0
  19. package/build/devserver/email/caps.js +0 -0
  20. package/build/devserver/email/config.d.ts +21 -0
  21. package/build/devserver/email/config.js +72 -0
  22. package/build/devserver/email/index.d.ts +25 -0
  23. package/build/devserver/email/index.js +57 -0
  24. package/build/devserver/email/providers.d.ts +12 -0
  25. package/build/devserver/email/providers.js +96 -0
  26. package/build/devserver/email/status.d.ts +10 -0
  27. package/build/devserver/email/status.js +11 -0
  28. package/build/devserver/email/validate.d.ts +2 -0
  29. package/build/devserver/email/validate.js +24 -0
  30. package/build/devserver/email/wire.d.ts +8 -0
  31. package/build/devserver/email/wire.js +32 -0
  32. package/build/devserver/env.js +5 -54
  33. package/build/devserver/host.js +22 -7
  34. package/build/devserver/index.d.ts +2 -0
  35. package/build/devserver/index.js +8 -0
  36. package/build/shared/.tsbuildinfo +1 -1
  37. package/build/shared/index.d.ts +13 -0
  38. package/docs/email.md +64 -22
  39. package/package.json +4 -2
  40. package/src/cli/create.ts +2 -2
  41. package/src/cli/doctor.ts +15 -0
  42. package/src/compiler/config.ts +14 -0
  43. package/src/compiler/email-preview.ts +305 -0
  44. package/src/compiler/emails.ts +82 -12
  45. package/src/compiler/index.ts +20 -0
  46. package/src/compiler/plugin.ts +88 -4
  47. package/src/compiler/vite.ts +4 -0
  48. package/src/devserver/dotenv.ts +94 -0
  49. package/src/devserver/email/caps.ts +0 -0
  50. package/src/devserver/email/config.ts +123 -0
  51. package/src/devserver/email/index.ts +111 -0
  52. package/src/devserver/email/providers.ts +130 -0
  53. package/src/devserver/email/status.ts +23 -0
  54. package/src/devserver/email/validate.ts +40 -0
  55. package/src/devserver/email/wire.ts +55 -0
  56. package/src/devserver/env.ts +8 -65
  57. package/src/devserver/host.ts +29 -12
  58. package/src/devserver/index.ts +20 -0
  59. package/src/shared/index.ts +36 -0
  60. package/test/devserver-email.test.ts +241 -0
  61. package/test/email-preview.test.ts +68 -0
  62. 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,10 @@
1
+ export declare enum EmailStatus {
2
+ Sent = 0,
3
+ Disabled = 1,
4
+ Budget = 2,
5
+ RecipientCapped = 3,
6
+ Deduped = 4,
7
+ TryLater = 5,
8
+ BadRecipient = 6,
9
+ ProviderError = 7
10
+ }
@@ -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,2 @@
1
+ export declare function validRecipient(s: string): boolean;
2
+ export declare function validFrom(s: string): boolean;
@@ -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,8 @@
1
+ export interface ParsedEmail {
2
+ readonly to: string;
3
+ readonly subject: string;
4
+ readonly purpose: string;
5
+ readonly body: string;
6
+ readonly html: string;
7
+ }
8
+ export declare function parseEmailBlob(raw: Buffer): ParsedEmail | null;
@@ -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
+ }
@@ -1,58 +1,9 @@
1
- import fs from 'node:fs';
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 e = load();
53
- return e.vars.has(key) ? e.vars.get(key) : null;
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 e = load();
57
- return e.secrets.has(key) ? e.secrets.get(key) : null;
7
+ const v = loadEnvFiles(process.cwd()).secrets.get(key);
8
+ return v === undefined ? null : v;
58
9
  }
@@ -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
- let to = '<unparsed>';
115
- if (raw.length >= 14) {
116
- const toLen = raw.readUInt16LE(0);
117
- if (14 + toLen <= raw.length)
118
- to = raw.toString('utf8', 14, 14 + toLen);
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
- process.stdout.write(` ✉ dev email_send -> ${to} (not actually sent)\n`);
121
- return 0;
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;
@@ -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"}
@@ -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` has no real provider: `EmailService.send` logs `✉ dev email_send ->
99
- <recipient> (not actually sent)` and returns `Sent`, so your flow proceeds. The
100
- ABI is identical to the edge, so code that runs in dev runs on the edge.
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.', // plain-text body
115
- 'welcome', // purpose tag (dedup / abuse keying)
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 | Meaning | Retry? |
128
- | --- | --- | --- |
129
- | `Sent` | Accepted by the provider | — |
130
- | `Deduped` | An identical recent `(recipient, purpose)` was collapsed | treat as sent |
131
- | `Budget` | The host's per-minute budget is exhausted | yes, later |
132
- | `TryLater` | The mailer was saturated / a queue was full | yes, back off |
133
- | `RecipientCapped` | The per-recipient hourly cap was hit | no (this window) |
134
- | `BadRecipient` | The address failed validation (CRLF, multiple addresses) | no |
135
- | `Disabled` | This host has no `[email]` capability | no |
136
- | `ProviderError` | The provider rejected it, or transport failed after retries | no |
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}}!', // subject
153
- 'Hi {{name}}, your code is {{code}}.', // plain-text body
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 width="100%" style={{ fontFamily: 'Arial, sans-serif' }}>
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>Your code is <b>{code}</b>.</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
- - **Use inline `style={{ ... }}`.** Email clients strip `<style>`/external CSS;
210
- inline styles render everywhere. A CSS file imported into the component is
211
- inlined for you at build (via `juice`).
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.44",
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.8",
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",