serverless-ircd 0.2.0 → 0.3.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/CHANGELOG.md +84 -0
- package/README.md +28 -19
- package/apps/cf-worker/package.json +1 -1
- package/apps/cf-worker/scripts/smoke.mjs +0 -0
- package/apps/cf-worker/src/worker.ts +8 -2
- package/apps/cf-worker/tests/smoke.test.ts +1 -0
- package/apps/cf-worker/wrangler.test.toml +5 -1
- package/apps/cf-worker/wrangler.toml +41 -17
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +1 -0
- package/apps/local-cli/src/main.ts +1 -1
- package/apps/local-cli/src/server.ts +28 -2
- package/apps/local-cli/tests/e2e.test.ts +52 -0
- package/docs/ADR-001-pure-reducers-and-effect-system.md +74 -0
- package/docs/ADR-002-location-of-authority.md +82 -0
- package/docs/ADR-003-durable-object-sharding.md +93 -0
- package/docs/ADR-004-dynamodb-schema.md +96 -0
- package/docs/ADR-005-wss-only-transport-v1.md +83 -0
- package/docs/ADR-006-sasl-mechanism-scope.md +86 -0
- package/docs/ADR-007-deterministic-ports.md +82 -0
- package/docs/ADR-008-monorepo-tooling.md +60 -0
- package/docs/AWS-Adapter-Architecture.md +6 -4
- package/docs/AWS-Deployment.md +86 -7
- package/docs/Cloudflare-Deployment-Guide.md +5 -5
- package/docs/Home.md +11 -1
- package/docs/Release-Process.md +9 -6
- package/package.json +14 -15
- package/packages/aws-adapter/package.json +1 -1
- package/packages/aws-adapter/src/account-store.ts +121 -0
- package/packages/aws-adapter/src/aws-runtime.ts +118 -30
- package/packages/aws-adapter/src/cdk-table-defs.ts +5 -1
- package/packages/aws-adapter/src/config-loader.ts +30 -0
- package/packages/aws-adapter/src/dynamo-account-store.ts +156 -0
- package/packages/aws-adapter/src/handlers/default.ts +8 -0
- package/packages/aws-adapter/src/handlers/disconnect.ts +27 -8
- package/packages/aws-adapter/src/handlers/index.ts +51 -6
- package/packages/aws-adapter/src/index.ts +7 -0
- package/packages/aws-adapter/tests/account-store-dynamo.test.ts +171 -0
- package/packages/aws-adapter/tests/account-store.test.ts +280 -0
- package/packages/aws-adapter/tests/config-loader.test.ts +55 -0
- package/packages/aws-adapter/tests/disconnect-fanout.test.ts +336 -0
- package/packages/aws-adapter/tests/handlers.test.ts +46 -0
- package/packages/cf-adapter/package.json +1 -1
- package/packages/cf-adapter/src/cf-runtime.ts +42 -22
- package/packages/cf-adapter/src/channel-do.ts +40 -1
- package/packages/cf-adapter/src/channel-registry-do.ts +58 -0
- package/packages/cf-adapter/src/config-loader.ts +29 -0
- package/packages/cf-adapter/src/connection-do.ts +85 -0
- package/packages/cf-adapter/src/env.ts +27 -1
- package/packages/cf-adapter/src/index.ts +2 -1
- package/packages/cf-adapter/tests/cf-runtime.test.ts +91 -0
- package/packages/cf-adapter/tests/connection-do.test.ts +40 -0
- package/packages/cf-adapter/tests/worker/main.ts +2 -1
- package/packages/cf-adapter/tests/worker/stubs/channel-stub.ts +7 -1
- package/packages/cf-adapter/wrangler.test.toml +10 -1
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/in-memory-runtime/src/in-memory-runtime.ts +14 -28
- package/packages/in-memory-runtime/tests/in-memory-runtime.test.ts +19 -0
- package/packages/irc-core/package.json +3 -2
- package/packages/irc-core/src/case-fold.ts +64 -0
- package/packages/irc-core/src/commands/index.ts +2 -0
- package/packages/irc-core/src/commands/invite.ts +6 -9
- package/packages/irc-core/src/commands/isupport.ts +1 -1
- package/packages/irc-core/src/commands/join.ts +4 -3
- package/packages/irc-core/src/commands/kick.ts +4 -11
- package/packages/irc-core/src/commands/list.ts +5 -4
- package/packages/irc-core/src/commands/mode.ts +5 -9
- package/packages/irc-core/src/commands/motd-lines.ts +127 -0
- package/packages/irc-core/src/commands/motd.ts +6 -110
- package/packages/irc-core/src/commands/oper.ts +151 -0
- package/packages/irc-core/src/commands/registration.ts +8 -7
- package/packages/irc-core/src/commands/tagmsg.ts +205 -0
- package/packages/irc-core/src/config.ts +26 -3
- package/packages/irc-core/src/index.ts +2 -0
- package/packages/irc-core/src/ports.ts +68 -4
- package/packages/irc-core/src/types.ts +22 -0
- package/packages/irc-core/tests/account-store.test.ts +88 -0
- package/packages/irc-core/tests/case-fold.test.ts +80 -0
- package/packages/irc-core/tests/commands/kick.test.ts +15 -0
- package/packages/irc-core/tests/commands/oper.test.ts +257 -0
- package/packages/irc-core/tests/commands/registration.test.ts +106 -14
- package/packages/irc-core/tests/commands/tagmsg.test.ts +688 -0
- package/packages/irc-core/tests/commands/who.test.ts +22 -4
- package/packages/irc-core/tests/commands/whois.test.ts +8 -1
- package/packages/irc-core/tests/config.test.ts +75 -0
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +24 -0
- package/packages/irc-server/src/routing.ts +9 -0
- package/packages/irc-server/tests/actor.test.ts +220 -1
- package/packages/irc-server/tests/routing.test.ts +3 -0
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/in-memory-harness.ts +5 -4
- package/pnpm-workspace.yaml +1 -0
- package/tools/seed-aws-accounts.ts +80 -0
- package/AGENTS.md +0 -5
- package/MOTD.txt +0 -3
- package/PLAN-FIXES.md +0 -358
- package/PLAN.md +0 -420
- package/dashboards/cloudwatch-irc.json +0 -139
- package/progress.md +0 -107
- package/tickets.md +0 -2485
- package/webircgateway/LICENSE +0 -201
- package/webircgateway/Makefile +0 -44
- package/webircgateway/README.md +0 -134
- package/webircgateway/config.conf.example +0 -135
- package/webircgateway/go.mod +0 -16
- package/webircgateway/go.sum +0 -89
- package/webircgateway/main.go +0 -118
- package/webircgateway/pkg/dnsbl/dnsbl.go +0 -121
- package/webircgateway/pkg/identd/identd.go +0 -86
- package/webircgateway/pkg/identd/rpcclient.go +0 -59
- package/webircgateway/pkg/irc/isupport.go +0 -56
- package/webircgateway/pkg/irc/message.go +0 -217
- package/webircgateway/pkg/irc/state.go +0 -79
- package/webircgateway/pkg/proxy/proxy.go +0 -129
- package/webircgateway/pkg/proxy/server.go +0 -237
- package/webircgateway/pkg/recaptcha/recaptcha.go +0 -59
- package/webircgateway/pkg/webircgateway/client.go +0 -741
- package/webircgateway/pkg/webircgateway/client_command_handlers.go +0 -495
- package/webircgateway/pkg/webircgateway/config.go +0 -385
- package/webircgateway/pkg/webircgateway/gateway.go +0 -278
- package/webircgateway/pkg/webircgateway/gateway_utils.go +0 -133
- package/webircgateway/pkg/webircgateway/hooks.go +0 -152
- package/webircgateway/pkg/webircgateway/letsencrypt.go +0 -41
- package/webircgateway/pkg/webircgateway/messagetags.go +0 -103
- package/webircgateway/pkg/webircgateway/transport_kiwiirc.go +0 -206
- package/webircgateway/pkg/webircgateway/transport_sockjs.go +0 -107
- package/webircgateway/pkg/webircgateway/transport_tcp.go +0 -113
- package/webircgateway/pkg/webircgateway/transport_websocket.go +0 -126
- package/webircgateway/pkg/webircgateway/utils.go +0 -147
- package/webircgateway/plugins/example/plugin.go +0 -11
- package/webircgateway/plugins/stats/plugin.go +0 -52
- package/webircgateway/staticcheck.conf +0 -1
- package/webircgateway/webircgateway.svg +0 -3
|
@@ -11,7 +11,13 @@ import {
|
|
|
11
11
|
} from '../../src/commands/registration';
|
|
12
12
|
import { Effect } from '../../src/effects';
|
|
13
13
|
import type { Effect as EffectType, RawLine } from '../../src/effects';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
EmptyMotdProvider,
|
|
16
|
+
FakeClock,
|
|
17
|
+
SequentialIdFactory,
|
|
18
|
+
StaticMotdProvider,
|
|
19
|
+
} from '../../src/ports';
|
|
20
|
+
import type { MotdProvider } from '../../src/ports';
|
|
15
21
|
import { type ConnectionState, createConnection } from '../../src/state/connection';
|
|
16
22
|
import { type Ctx, type ServerConfig, buildCtx } from '../../src/types';
|
|
17
23
|
|
|
@@ -27,12 +33,16 @@ const serverConfig: ServerConfig = {
|
|
|
27
33
|
quitMessage: 'Client Quit',
|
|
28
34
|
};
|
|
29
35
|
|
|
30
|
-
function makeCtx(
|
|
36
|
+
function makeCtx(
|
|
37
|
+
state: ConnectionState,
|
|
38
|
+
clock = new FakeClock(1_000),
|
|
39
|
+
motd: MotdProvider = EmptyMotdProvider,
|
|
40
|
+
): Ctx {
|
|
31
41
|
return buildCtx({
|
|
32
42
|
serverConfig,
|
|
33
43
|
clock,
|
|
34
44
|
ids: new SequentialIdFactory(),
|
|
35
|
-
motd
|
|
45
|
+
motd,
|
|
36
46
|
connection: state,
|
|
37
47
|
});
|
|
38
48
|
}
|
|
@@ -80,9 +90,7 @@ function expectedWelcome(nick: string, user: string, host: string | undefined):
|
|
|
80
90
|
L(`${prefix} 003 ${nick} :This server was created in Phase 1`),
|
|
81
91
|
L(`${prefix} 004 ${nick} ${sv} 0.2.0 iosw biklmnstp`),
|
|
82
92
|
...isupport,
|
|
83
|
-
L(`${prefix}
|
|
84
|
-
L(`${prefix} 372 ${nick} :- MOTD placeholder`),
|
|
85
|
-
L(`${prefix} 376 ${nick} :End of MOTD command`),
|
|
93
|
+
L(`${prefix} 422 ${nick} :MOTD File is missing`),
|
|
86
94
|
];
|
|
87
95
|
}
|
|
88
96
|
|
|
@@ -489,19 +497,22 @@ describe('passReducer', () => {
|
|
|
489
497
|
// ============================================================================
|
|
490
498
|
|
|
491
499
|
describe('buildWelcomeLines', () => {
|
|
492
|
-
it('produces the welcome block in the correct numeric order', () => {
|
|
500
|
+
it('produces the welcome block in the correct numeric order with MOTD content', () => {
|
|
493
501
|
const state = makeState();
|
|
494
502
|
state.nick = 'alice';
|
|
495
503
|
state.user = 'alice';
|
|
496
504
|
state.host = 'example.com';
|
|
497
|
-
const lines = buildWelcomeLines(
|
|
498
|
-
|
|
505
|
+
const lines = buildWelcomeLines(
|
|
506
|
+
state,
|
|
507
|
+
makeCtx(state, new FakeClock(1_000), new StaticMotdProvider(['line one', 'line two'])),
|
|
508
|
+
);
|
|
499
509
|
expect(lines.length).toBeGreaterThanOrEqual(8);
|
|
500
510
|
const codes = lines.map((l) => l.text.split(' ')[1]);
|
|
501
|
-
// First four numerics in fixed order; 005 (>=1);
|
|
511
|
+
// First four numerics in fixed order; 005 (>=1); MOTD block last.
|
|
502
512
|
expect(codes.slice(0, 4)).toEqual(['001', '002', '003', '004']);
|
|
503
513
|
expect(codes[4]).toBe('005');
|
|
504
|
-
expect(codes.at(-
|
|
514
|
+
expect(codes.at(-4)).toBe('375');
|
|
515
|
+
expect(codes.at(-3)).toBe('372');
|
|
505
516
|
expect(codes.at(-2)).toBe('372');
|
|
506
517
|
expect(codes.at(-1)).toBe('376');
|
|
507
518
|
});
|
|
@@ -557,6 +568,89 @@ describe('buildWelcomeLines', () => {
|
|
|
557
568
|
});
|
|
558
569
|
});
|
|
559
570
|
|
|
571
|
+
// ============================================================================
|
|
572
|
+
// buildWelcomeLines — MOTD driven from MotdProvider
|
|
573
|
+
// ============================================================================
|
|
574
|
+
|
|
575
|
+
describe('buildWelcomeLines — MOTD content', () => {
|
|
576
|
+
it('emits one 372 per MOTD line sourced from the MotdProvider', () => {
|
|
577
|
+
const state = makeState();
|
|
578
|
+
state.nick = 'alice';
|
|
579
|
+
state.user = 'alice';
|
|
580
|
+
state.host = 'example.com';
|
|
581
|
+
const lines = buildWelcomeLines(
|
|
582
|
+
state,
|
|
583
|
+
makeCtx(state, new FakeClock(1_000), new StaticMotdProvider(['line A', 'line B'])),
|
|
584
|
+
);
|
|
585
|
+
const rpl372 = lines.filter((l) => l.text.split(' ')[1] === '372');
|
|
586
|
+
expect(rpl372).toHaveLength(2);
|
|
587
|
+
expect(rpl372[0]).toEqual(L(':irc.example.com 372 alice :- line A'));
|
|
588
|
+
expect(rpl372[1]).toEqual(L(':irc.example.com 372 alice :- line B'));
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
it('frames the MOTD content with 375 start and 376 end', () => {
|
|
592
|
+
const state = makeState();
|
|
593
|
+
state.nick = 'alice';
|
|
594
|
+
state.user = 'alice';
|
|
595
|
+
const lines = buildWelcomeLines(
|
|
596
|
+
state,
|
|
597
|
+
makeCtx(state, new FakeClock(1_000), new StaticMotdProvider(['hi'])),
|
|
598
|
+
);
|
|
599
|
+
expect(lines).toContainEqual(
|
|
600
|
+
L(':irc.example.com 375 alice :- irc.example.com Message of the day -'),
|
|
601
|
+
);
|
|
602
|
+
expect(lines).toContainEqual(L(':irc.example.com 372 alice :- hi'));
|
|
603
|
+
expect(lines).toContainEqual(L(':irc.example.com 376 alice :End of MOTD command'));
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
it('reads MOTD lines fresh from the provider on each call', () => {
|
|
607
|
+
const state = makeState();
|
|
608
|
+
state.nick = 'alice';
|
|
609
|
+
state.user = 'alice';
|
|
610
|
+
const provider = new StaticMotdProvider(['first']);
|
|
611
|
+
const ctx = makeCtx(state, new FakeClock(1_000), provider);
|
|
612
|
+
const first = buildWelcomeLines(state, ctx).filter((l) => l.text.split(' ')[1] === '372');
|
|
613
|
+
provider.setLines(['first', 'second']);
|
|
614
|
+
const second = buildWelcomeLines(state, ctx).filter((l) => l.text.split(' ')[1] === '372');
|
|
615
|
+
expect(first).toHaveLength(1);
|
|
616
|
+
expect(second).toHaveLength(2);
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
it('splits over-long MOTD lines so every emitted line fits the 510-byte budget', () => {
|
|
620
|
+
const state = makeState();
|
|
621
|
+
state.nick = 'alice';
|
|
622
|
+
state.user = 'alice';
|
|
623
|
+
state.host = 'example.com';
|
|
624
|
+
const long = 'A'.repeat(2_000);
|
|
625
|
+
const lines = buildWelcomeLines(
|
|
626
|
+
state,
|
|
627
|
+
makeCtx(state, new FakeClock(1_000), new StaticMotdProvider([long])),
|
|
628
|
+
);
|
|
629
|
+
for (const line of lines) {
|
|
630
|
+
expect(line.text.length).toBeLessThanOrEqual(510);
|
|
631
|
+
}
|
|
632
|
+
// The single over-long line is hard-wrapped into many 372 chunks.
|
|
633
|
+
const rpl372 = lines.filter((l) => l.text.split(' ')[1] === '372');
|
|
634
|
+
expect(rpl372.length).toBeGreaterThan(1);
|
|
635
|
+
});
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
describe('buildWelcomeLines — empty MOTD', () => {
|
|
639
|
+
it('emits 422 ERR_NOMOTD instead of a placeholder when no MOTD is configured', () => {
|
|
640
|
+
const state = makeState();
|
|
641
|
+
state.nick = 'alice';
|
|
642
|
+
state.user = 'alice';
|
|
643
|
+
const lines = buildWelcomeLines(state, makeCtx(state));
|
|
644
|
+
const codes = lines.map((l) => l.text.split(' ')[1]);
|
|
645
|
+
expect(codes.at(-1)).toBe('422');
|
|
646
|
+
expect(lines).toContainEqual(L(':irc.example.com 422 alice :MOTD File is missing'));
|
|
647
|
+
// No MOTD framing numerics leak through on the empty path.
|
|
648
|
+
expect(codes).not.toContain('375');
|
|
649
|
+
expect(codes).not.toContain('372');
|
|
650
|
+
expect(codes).not.toContain('376');
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
|
|
560
654
|
// ============================================================================
|
|
561
655
|
// emitWelcomeIfReady
|
|
562
656
|
// ============================================================================
|
|
@@ -860,8 +954,6 @@ function expectedWelcomeWithHost(nick: string, hostmask: string): RawLine[] {
|
|
|
860
954
|
L(`${prefix} 003 ${nick} :This server was created in Phase 1`),
|
|
861
955
|
L(`${prefix} 004 ${nick} ${sv} 0.2.0 iosw biklmnstp`),
|
|
862
956
|
...isupport,
|
|
863
|
-
L(`${prefix}
|
|
864
|
-
L(`${prefix} 372 ${nick} :- MOTD placeholder`),
|
|
865
|
-
L(`${prefix} 376 ${nick} :End of MOTD command`),
|
|
957
|
+
L(`${prefix} 422 ${nick} :MOTD File is missing`),
|
|
866
958
|
];
|
|
867
959
|
}
|