@volter/twin-gmail 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +35 -2
- package/dist/generated/pack-facts.json +47 -0
- package/dist/journeys/customer-life.json +1013 -0
- package/dist/journeys/decisions.json +77 -0
- package/dist/journeys/demand.json +43 -0
- package/dist/journeys/first-use.json +24 -0
- package/dist/journeys/vendor-examples.json +7 -0
- package/dist/spec/SOURCE.md +13 -0
- package/dist/spec/rfc5321.txt +5323 -0
- package/dist/spec/sources.json +25 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +19 -0
- package/dist/src/engine/mail.d.ts +19 -0
- package/dist/src/engine/mail.js +89 -0
- package/dist/src/fetch.d.ts +2 -0
- package/dist/src/fetch.js +18 -0
- package/dist/src/generated/surface.gen.json +1 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +11 -0
- package/dist/src/manifest.d.ts +2 -0
- package/dist/src/manifest.js +68 -0
- package/dist/src/screens/home.d.ts +2 -0
- package/dist/src/screens/home.js +3 -0
- package/dist/src/screens/mail.d.ts +2 -0
- package/dist/src/screens/mail.js +142 -0
- package/dist/src/semantics/around.d.ts +2 -0
- package/dist/src/semantics/around.js +97 -0
- package/dist/src/semantics/index.d.ts +2 -0
- package/dist/src/semantics/index.js +1 -0
- package/dist/src/semantics/shared.d.ts +52 -0
- package/dist/src/semantics/shared.js +97 -0
- package/dist/src/semantics/states.d.ts +4 -0
- package/dist/src/semantics/states.js +33 -0
- package/dist/src/server.d.ts +9 -0
- package/dist/src/server.js +23 -0
- package/dist/src/stream.d.ts +2 -0
- package/dist/src/stream.js +10 -0
- package/generated/pack-facts.json +47 -0
- package/journeys/customer-life.json +1013 -0
- package/journeys/decisions.json +77 -0
- package/journeys/demand.json +43 -0
- package/journeys/first-use.json +24 -0
- package/journeys/vendor-examples.json +7 -0
- package/package.json +52 -4
- package/spec/SOURCE.md +13 -0
- package/spec/rfc5321.txt +5323 -0
- package/spec/sources.json +25 -0
- package/src/cli.ts +20 -0
- package/src/engine/mail.ts +76 -0
- package/src/fetch.ts +19 -0
- package/src/generated/surface.gen.json +1 -0
- package/src/index.ts +13 -0
- package/src/manifest.ts +86 -0
- package/src/screens/home.ts +6 -0
- package/src/screens/mail.ts +128 -0
- package/src/semantics/around.ts +91 -0
- package/src/semantics/index.ts +6 -0
- package/src/semantics/shared.ts +111 -0
- package/src/semantics/states.ts +41 -0
- package/src/server.ts +19 -0
- package/src/stream.ts +11 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Gmail's receiving server's state machine (docs/contributing/architecture.md, "What an author writes": states): a
|
|
2
|
+
// session's stage, each move cited from RFC 5321. The front (./around.ts) asks it for every command, so the declaration
|
|
3
|
+
// is the rule. A receiving server asks no authentication, so a session has no AUTH stage.
|
|
4
|
+
import type { StateField, Transition } from '@volter/world-core';
|
|
5
|
+
|
|
6
|
+
const RFC5321 = (section: string, quote: string): string => `spec:${section} "${quote}"`;
|
|
7
|
+
// Where the documentation stops: Gmail's words for a command out of order, recalled from its server's replies
|
|
8
|
+
const badSequence = (message: string) => ({ status: 503, code: '5.5.1', message });
|
|
9
|
+
|
|
10
|
+
const ORDER = 'Once started, a mail transaction consists of a transaction beginning command, one or more RCPT commands, and a DATA command, in that order.';
|
|
11
|
+
|
|
12
|
+
// fresh (nothing yet), helo or ehlo (greeted), mail (a sender), rcpt (recipients), data (the message's lines), closed
|
|
13
|
+
const stage: Transition[] = [
|
|
14
|
+
{ operation: 'EHLO', from: '*', to: 'ehlo', source: RFC5321('4.1.4', 'If it is issued after the session begins and the EHLO command is acceptable to the SMTP server, the SMTP server MUST clear all buffers and reset the state exactly as if a RSET command had been issued.') },
|
|
15
|
+
{ operation: 'HELO', from: '*', to: 'helo', source: RFC5321('4.1.1.1', 'These commands are used to identify the SMTP client to the SMTP server.') },
|
|
16
|
+
{
|
|
17
|
+
operation: 'MAIL', from: ['helo', 'ehlo'], to: 'mail',
|
|
18
|
+
refusals: { fresh: badSequence('EHLO/HELO first. - gsmtp'), mail: badSequence('Nested MAIL command. - gsmtp'), rcpt: badSequence('Nested MAIL command. - gsmtp') },
|
|
19
|
+
source: RFC5321('4.1.4', 'MAIL (or SEND, SOML, or SAML) MUST NOT be sent if a mail transaction is already open'),
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
operation: 'RCPT', from: ['mail', 'rcpt'], to: 'rcpt',
|
|
23
|
+
refusals: { fresh: badSequence('MAIL first. - gsmtp'), helo: badSequence('MAIL first. - gsmtp'), ehlo: badSequence('MAIL first. - gsmtp') },
|
|
24
|
+
source: RFC5321('4.1.4', ORDER),
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
operation: 'DATA', from: ['rcpt'], to: 'data',
|
|
28
|
+
refusals: { fresh: badSequence('MAIL first. - gsmtp'), helo: badSequence('MAIL first. - gsmtp'), ehlo: badSequence('MAIL first. - gsmtp'), mail: badSequence('RCPT first. - gsmtp') },
|
|
29
|
+
source: RFC5321('4.1.4', ORDER),
|
|
30
|
+
},
|
|
31
|
+
// the end-of-data dot closes the transaction, back to the greeting the session had
|
|
32
|
+
{ operation: 'DATA', from: ['data'], to: 'ehlo', source: RFC5321('4.1.4', 'or if the previous one successfully concluded with a successful DATA command') },
|
|
33
|
+
{ operation: 'DATA', from: ['data'], to: 'helo', source: RFC5321('4.1.4', 'or if the previous one successfully concluded with a successful DATA command') },
|
|
34
|
+
{ operation: 'RSET', from: ['mail', 'rcpt'], to: 'ehlo', source: RFC5321('4.1.1.5', 'This command specifies that the current mail transaction will be aborted.') },
|
|
35
|
+
{ operation: 'RSET', from: ['mail', 'rcpt'], to: 'helo', source: RFC5321('4.1.1.5', 'This command specifies that the current mail transaction will be aborted.') },
|
|
36
|
+
{ operation: 'QUIT', from: '*', to: 'closed', source: RFC5321('4.1.1.10', 'The QUIT command may be issued at any time.') },
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
export const states: Record<string, { state: Record<string, StateField> }> = {
|
|
40
|
+
Session: { state: { stage: { initial: 'fresh', transitions: stage } } },
|
|
41
|
+
};
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// FETCH-FIRST (runtime contract R12b) with a wire of the pack's own: the webmail on the main port (the pack's fetch) and
|
|
2
|
+
// Gmail's receiving SMTP server on its side port (`smtpPort`, the manifest's sidePorts: the kernel's `serveStream`
|
|
3
|
+
// carrying the byte-stream door's connections), where the World's senders hand gmail.com's mail (ctx.mail). A listener
|
|
4
|
+
// that fails to open releases the one already open; stop closes both and settles once both have.
|
|
5
|
+
import { serveHttp, serveStream, type PackFetchOptions } from '@volter/world-core';
|
|
6
|
+
import { createGmailFetch } from './fetch.ts';
|
|
7
|
+
import { createGmailTwinStream } from './stream.ts';
|
|
8
|
+
|
|
9
|
+
export async function createGmailTwinServer(options: PackFetchOptions & { port?: number; smtpPort?: number } = {}): Promise<{ port: number; smtpPort: number; stop: () => Promise<void> }> {
|
|
10
|
+
const server = await serveHttp({ port: options.port ?? 0, idleTimeout: 60, fetch: createGmailFetch(options) });
|
|
11
|
+
let smtp: Awaited<ReturnType<typeof serveStream>>;
|
|
12
|
+
try { smtp = await serveStream({ port: options.smtpPort ?? 0, stream: createGmailTwinStream(options) }); }
|
|
13
|
+
catch (error) { await server.stop(true); throw error; }
|
|
14
|
+
return {
|
|
15
|
+
port: server.port ?? options.port ?? 0,
|
|
16
|
+
smtpPort: smtp.port,
|
|
17
|
+
stop: async () => { await Promise.all([smtp.stop(), server.stop(true)]); },
|
|
18
|
+
};
|
|
19
|
+
}
|
package/src/stream.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// GMAIL'S RECEIVING SERVER'S BYTE-STREAM DOOR (world-core's TwinStream): a connection's bytes framed and carried to the
|
|
2
|
+
// server's front (./semantics/around.ts) by the kernel's SMTP server machine (`smtpServer.smtpStream`, RFC 5321
|
|
3
|
+
// §4.1.1.4 and §4.5.2). The host carries the bytes: a local listener's socket on the pack's SMTP side port (./server.ts),
|
|
4
|
+
// or the World's sender in process (a journey walk's companion).
|
|
5
|
+
import { smtpServer, type PackFetchOptions, type TwinStream } from '@volter/world-core';
|
|
6
|
+
import { createGmailFetch } from './fetch.ts';
|
|
7
|
+
import { BRIDGE } from './semantics/shared.ts';
|
|
8
|
+
|
|
9
|
+
export function createGmailTwinStream(options: PackFetchOptions = {}): TwinStream {
|
|
10
|
+
return smtpServer.smtpStream(createGmailFetch(options), BRIDGE);
|
|
11
|
+
}
|