@adamancyzhang/claude-orchestrator 0.4.0 → 0.4.1

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 (104) hide show
  1. package/README.md +19 -13
  2. package/dist/config.d.ts +17 -0
  3. package/dist/config.js +20 -2
  4. package/dist/config.js.map +1 -1
  5. package/dist/email-validator/__tests__/domain.test.d.ts +1 -0
  6. package/dist/email-validator/__tests__/domain.test.js +128 -0
  7. package/dist/email-validator/__tests__/domain.test.js.map +1 -0
  8. package/dist/email-validator/__tests__/local-part.test.d.ts +1 -0
  9. package/dist/email-validator/__tests__/local-part.test.js +110 -0
  10. package/dist/email-validator/__tests__/local-part.test.js.map +1 -0
  11. package/dist/email-validator/__tests__/parser.test.d.ts +1 -0
  12. package/dist/email-validator/__tests__/parser.test.js +161 -0
  13. package/dist/email-validator/__tests__/parser.test.js.map +1 -0
  14. package/dist/email-validator/__tests__/validator.test.d.ts +1 -0
  15. package/dist/email-validator/__tests__/validator.test.js +152 -0
  16. package/dist/email-validator/__tests__/validator.test.js.map +1 -0
  17. package/dist/email-validator/diagnostics.d.ts +3 -0
  18. package/dist/email-validator/diagnostics.js +34 -0
  19. package/dist/email-validator/diagnostics.js.map +1 -0
  20. package/dist/email-validator/domain.d.ts +2 -0
  21. package/dist/email-validator/domain.js +119 -0
  22. package/dist/email-validator/domain.js.map +1 -0
  23. package/dist/email-validator/index.d.ts +2 -0
  24. package/dist/email-validator/index.js +2 -0
  25. package/dist/email-validator/index.js.map +1 -0
  26. package/dist/email-validator/local-part.d.ts +2 -0
  27. package/dist/email-validator/local-part.js +67 -0
  28. package/dist/email-validator/local-part.js.map +1 -0
  29. package/dist/email-validator/parser.d.ts +9 -0
  30. package/dist/email-validator/parser.js +120 -0
  31. package/dist/email-validator/parser.js.map +1 -0
  32. package/dist/email-validator/types.d.ts +24 -0
  33. package/dist/email-validator/types.js +2 -0
  34. package/dist/email-validator/types.js.map +1 -0
  35. package/dist/email-validator/validator.d.ts +9 -0
  36. package/dist/email-validator/validator.js +51 -0
  37. package/dist/email-validator/validator.js.map +1 -0
  38. package/dist/executor/runner.d.ts +19 -2
  39. package/dist/executor/runner.js +29 -4
  40. package/dist/executor/runner.js.map +1 -1
  41. package/dist/executor/template.d.ts +3 -1
  42. package/dist/executor/template.js +17 -32
  43. package/dist/executor/template.js.map +1 -1
  44. package/dist/index.js +3 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/leader/chain-router.d.ts +6 -2
  47. package/dist/leader/chain-router.js +81 -12
  48. package/dist/leader/chain-router.js.map +1 -1
  49. package/dist/leader/event-bus.d.ts +1 -1
  50. package/dist/leader/event-bus.js +1 -0
  51. package/dist/leader/event-bus.js.map +1 -1
  52. package/dist/leader/index.js +97 -9
  53. package/dist/leader/index.js.map +1 -1
  54. package/dist/leader/merge-validator.d.ts +2 -1
  55. package/dist/leader/merge-validator.js +13 -20
  56. package/dist/leader/merge-validator.js.map +1 -1
  57. package/dist/leader/state.d.ts +3 -0
  58. package/dist/leader/state.js +30 -1
  59. package/dist/leader/state.js.map +1 -1
  60. package/dist/leader/stream-tailer.d.ts +12 -0
  61. package/dist/leader/stream-tailer.js +58 -0
  62. package/dist/leader/stream-tailer.js.map +1 -0
  63. package/dist/leader/tui.d.ts +6 -0
  64. package/dist/leader/tui.js +131 -27
  65. package/dist/leader/tui.js.map +1 -1
  66. package/dist/models/schemas.d.ts +13 -6
  67. package/dist/models/schemas.js +3 -0
  68. package/dist/models/schemas.js.map +1 -1
  69. package/dist/modules/task-queue.js +4 -0
  70. package/dist/modules/task-queue.js.map +1 -1
  71. package/dist/orchestrator/init-checker.d.ts +33 -0
  72. package/dist/orchestrator/init-checker.js +297 -0
  73. package/dist/orchestrator/init-checker.js.map +1 -0
  74. package/dist/orchestrator/run.d.ts +1 -0
  75. package/dist/orchestrator/run.js +26 -81
  76. package/dist/orchestrator/run.js.map +1 -1
  77. package/dist/templates/agents/worker-commit-message.md +12 -0
  78. package/dist/templates/agents/worker-evaluate-format-hint.md +7 -0
  79. package/dist/templates/agents/worker-identity.md +7 -0
  80. package/dist/templates/agents/worker-merge-decision.md +12 -0
  81. package/dist/templates/agents/worker-task-doc.md +13 -0
  82. package/dist/templates/user-global-claude.md +65 -0
  83. package/dist/utils/console-capture.d.ts +2 -0
  84. package/dist/utils/console-capture.js +42 -0
  85. package/dist/utils/console-capture.js.map +1 -0
  86. package/dist/utils/exec.d.ts +2 -6
  87. package/dist/utils/exec.js +48 -29
  88. package/dist/utils/exec.js.map +1 -1
  89. package/dist/utils/json.d.ts +5 -0
  90. package/dist/utils/json.js +17 -0
  91. package/dist/utils/json.js.map +1 -0
  92. package/dist/worker/child-runner.js +19 -3
  93. package/dist/worker/child-runner.js.map +1 -1
  94. package/dist/worker/commit-checker.d.ts +3 -2
  95. package/dist/worker/commit-checker.js +13 -19
  96. package/dist/worker/commit-checker.js.map +1 -1
  97. package/dist/worker/evaluator.d.ts +3 -4
  98. package/dist/worker/evaluator.js +15 -50
  99. package/dist/worker/evaluator.js.map +1 -1
  100. package/dist/worker/watcher.js +9 -11
  101. package/dist/worker/watcher.js.map +1 -1
  102. package/dist/worker/worktree-initializer.js +3 -0
  103. package/dist/worker/worktree-initializer.js.map +1 -1
  104. package/package.json +1 -1
package/README.md CHANGED
@@ -73,7 +73,7 @@ Type a requirement in the TUI input line and press Enter. The Leader forwards it
73
73
  # CLI commands (from any terminal)
74
74
  claude-orchestrator push-task --title "Implement login endpoint" --priority 0
75
75
  claude-orchestrator send-message --to-name Jerry --content "Starting on the auth module?"
76
- claude-orchestrator list-tasks --status pending
76
+ claude-orchestrator poll-task --status pending
77
77
  ```
78
78
 
79
79
  ---
@@ -84,7 +84,7 @@ claude-orchestrator list-tasks --status pending
84
84
 
85
85
  | Component | What it does | ZK magic |
86
86
  |-----------|-------------|----------|
87
- | **Leader** | Read-only TUI, mechanical message/task routing, merge validation, orphan recovery. No AI calls. | `/leader` EPHEMERAL — exactly one Leader |
87
+ | **Leader** | Read-only TUI, mechanical message/task routing, merge validation, orphan recovery. Self-processes decompose via claude-cli when template available; otherwise forwards to Planner. | `/leader` EPHEMERAL — exactly one Leader |
88
88
  | **Worker** | Isolated git worktree, ZK watch loop, auto-processes messages via `claude -p`, self-evaluates output, auto-commits changes | EPHEMERAL nodes → auto-cleanup on disconnect |
89
89
  | **Task Queue** | Push → Claim → Complete (or Block/Fail/Retry). Role-link priority sorting. | Sequential nodes for FIFO, ephemeral claims for atomic locks |
90
90
  | **Message Router** | Point-to-point messaging via ZK watches | Persistent-sequential nodes, push notification |
@@ -99,7 +99,7 @@ Each Worker runs in its own `git worktree` under `.claude-orchestrator/worktree/
99
99
 
100
100
  ### CLI-Native — Zero MCP Server
101
101
 
102
- Leader and Workers each connect directly to ZooKeeper. The Leader is a pure router: forwards requirements, creates tasks from ChainDef JSON, mechanically executes EvalDecision JSON. All AI intelligence runs exclusively on Workers via `claude -p`. No HTTP, no SSE, no MCP protocol.
102
+ Leader and Workers each connect directly to ZooKeeper. The Leader is primarily a router: self-processes decompose when template available (otherwise forwards to Planner), creates tasks from ChainDef JSON, mechanically executes EvalDecision JSON. All other AI intelligence runs on Workers via `claude -p`. No HTTP, no SSE, no MCP protocol.
103
103
 
104
104
  ### Responsibility Chain
105
105
 
@@ -120,7 +120,7 @@ Each link is a dedicated role. One Worker produces, the next Worker verifies —
120
120
  | `push-task` | Create a task (optionally assign to someone) |
121
121
  | `claim-task` | Grab the next task — atomic, no two instances can claim the same one |
122
122
  | `complete-task` | Mark a task done with results |
123
- | `list-tasks` | List tasks by status |
123
+ | `poll-task` | List tasks, optionally filtered by status |
124
124
  | `task-block` | Mark a claimed task as blocked (with reason) |
125
125
  | `task-fail` | Mark a claimed task as failed (with reason) |
126
126
  | `task-retry` | Re-queue a failed task (retry_count + 1, max 3) |
@@ -249,7 +249,7 @@ node dist/index.js run --worker 3
249
249
  ### Run Tests
250
250
 
251
251
  ```bash
252
- npm test # All unit tests (110+)
252
+ npm test # Run all tests
253
253
  npm run test:watch # Watch mode
254
254
  npx vitest run tests/unit/worker-prompt-rendering.test.ts # Prompt rendering verification
255
255
  ```
@@ -267,14 +267,15 @@ npx vitest run tests/unit/worker-prompt-rendering.test.ts # Prompt rendering ve
267
267
  │ ├── leader/ # Leader node
268
268
  │ │ ├── index.ts # Startup / shutdown orchestration
269
269
  │ │ ├── tui.ts # ANSI TUI with Worker Messages panel
270
- │ │ ├── event-bus.ts # Typed EventEmitter (15 events)
270
+ │ │ ├── event-bus.ts # Typed EventEmitter (17 events)
271
271
  │ │ ├── state.ts # Centralized LeaderState
272
272
  │ │ ├── monitor.ts # WorkerMonitor — join/leave detection
273
273
  │ │ ├── orchestrator.ts # TaskOrchestrator — lifecycle tracking
274
274
  │ │ ├── recovery.ts # TaskRecovery — orphan recovery (max 3 retries)
275
275
  │ │ ├── watcher.ts # LeaderWatcher — message processing
276
- │ │ ├── chain-router.ts # ChainRouter — mechanical routing (no AI)
277
- │ │ └── merge-validator.ts # Cross-verify + merge worker branches
276
+ │ │ ├── chain-router.ts # ChainRouter — mechanical routing (self or forward decompose)
277
+ │ │ ├── merge-validator.ts # Cross-verify + merge worker branches
278
+ │ │ └── stream-tailer.ts # StreamTailer — live worker log display
278
279
  │ ├── worker/ # Worker node
279
280
  │ │ ├── worktree-initializer.ts# Name generation, worktree creation, role assignment
280
281
  │ │ ├── child.ts # Child process entry point
@@ -287,16 +288,21 @@ npx vitest run tests/unit/worker-prompt-rendering.test.ts # Prompt rendering ve
287
288
  │ │ └── runner.ts # ClaudeRunner — CLI execution wrapper
288
289
  │ ├── zk/
289
290
  │ │ ├── client.ts # ZooKeeper connection management
290
- │ │ └── paths.ts # ZK path constants
291
+ │ │ ├── paths.ts # ZK path constants
292
+ │ │ └── watcher.ts # ZK watch callback wrapper
291
293
  │ ├── modules/
292
294
  │ │ ├── registry.ts # Instance registry
293
295
  │ │ ├── task-queue.ts # Task queue (push/claim/complete/block/fail/retry)
294
296
  │ │ └── message-router.ts # Message routing + template rendering
297
+ │ ├── hooks/
298
+ │ │ └── engine.ts # HookEngine — pre/post lifecycle hooks
295
299
  │ ├── models/
296
300
  │ │ └── schemas.ts # Zod schemas (Instance, Task, Message, ChainDef, EvalDecision)
297
301
  │ └── utils/
298
- │ ├── exec.ts # Shell execution (execWithTee)
299
- └── logger.ts # Tagged logger (+ --debug mode)
302
+ │ ├── exec.ts # Shell execution (execWithTee, execWithStreaming, execAndCapture)
303
+ ├── logger.ts # Tagged logger (+ --debug mode)
304
+ │ ├── output.ts # JSON output helper
305
+ │ └── console-capture.ts # Console redirect to file (for TUI)
300
306
  ├── templates/ # Prompt and memory templates (v0.4)
301
307
  │ ├── agents/ # 7 Worker prompt templates
302
308
  │ └── claude-memory/ # 6 CLAUDE.md directory memory templates
@@ -317,9 +323,9 @@ npx vitest run tests/unit/worker-prompt-rendering.test.ts # Prompt rendering ve
317
323
  │ │ └── design.md # Worker initialization + directory memory design
318
324
  │ └── v0.3/ # Archived v0.3 docs
319
325
  ├── tests/
320
- │ ├── unit/ # 11 test files, 110+ tests
326
+ │ ├── unit/ # 11 test files
321
327
  │ │ └── worker-prompt-rendering.test.ts # Prompt variable substitution verification
322
- │ └── integration/ # Leader-Worker integration tests
328
+ │ └── integration/ # 7 integration test files
323
329
  ├── examples-workspace/ # Reference implementation of multi-agent patterns
324
330
  ├── docker-compose.yml # ZooKeeper
325
331
  ├── package.json
package/dist/config.d.ts CHANGED
@@ -14,6 +14,20 @@ export interface HooksConfig {
14
14
  worker_message_start?: string | null;
15
15
  worker_message_end?: string | null;
16
16
  }
17
+ export type StepAction = "created" | "updated" | "replaced" | "skipped" | "rejected" | "completed" | "failed";
18
+ export interface StepRecord {
19
+ action: StepAction;
20
+ timestamp: string;
21
+ reason?: string;
22
+ }
23
+ export interface InitStatus {
24
+ global_config?: StepRecord;
25
+ user_claude_md?: StepRecord;
26
+ team_claude_md?: StepRecord;
27
+ skills?: Record<string, StepRecord>;
28
+ worktrees?: Record<string, StepRecord>;
29
+ npm_install?: Record<string, StepRecord>;
30
+ }
17
31
  export interface InstanceConfig {
18
32
  instance_id?: string;
19
33
  name?: string;
@@ -29,6 +43,7 @@ export interface InstanceConfig {
29
43
  branch: string;
30
44
  instance_id: string;
31
45
  }>;
46
+ init_status?: InitStatus;
32
47
  }
33
48
  export interface ResolvedConfig {
34
49
  zk: ZkConfig;
@@ -63,4 +78,6 @@ export declare function saveProjectWorktreeConfig(entries: Record<string, Worktr
63
78
  branch: string;
64
79
  instanceId: string;
65
80
  }>): void;
81
+ export declare function loadInitStatus(): InitStatus;
82
+ export declare function saveInitStatusStep(stepId: string, record: StepRecord, subId?: string): void;
66
83
  export declare function expandHomeDir(p: string): string;
package/dist/config.js CHANGED
@@ -13,7 +13,7 @@ function defaultZkConfig() {
13
13
  return { url: "127.0.0.1:2181", root_path: "/claude-orchestrator", auth: null };
14
14
  }
15
15
  function defaultCacheDir() {
16
- return "~/.claude-orchestrator/sessions";
16
+ return ".claude-orchestrator/sessions";
17
17
  }
18
18
  function defaultCliCommand() {
19
19
  return "claude --dangerously-skip-permissions --permission-mode dontAsk";
@@ -32,7 +32,7 @@ export function loadConfig(cliOpts) {
32
32
  else if (process.env.ZK_HOSTS) {
33
33
  zk.url = process.env.ZK_HOSTS;
34
34
  }
35
- const cacheDir = global.cache_dir || defaultCacheDir();
35
+ const cacheDir = path.resolve(process.cwd(), expandHomeDir(global.cache_dir || defaultCacheDir()));
36
36
  // Command: project overrides global
37
37
  const mergedCommands = project.commands || global.commands;
38
38
  const cliCommand = mergedCommands?.["claude-cli"] || defaultCliCommand();
@@ -116,6 +116,24 @@ export function saveProjectWorktreeConfig(entries) {
116
116
  }
117
117
  saveInstanceConfig({ worktree: record }, false);
118
118
  }
119
+ export function loadInitStatus() {
120
+ const global = loadGlobalConfig();
121
+ return global.init_status ?? {};
122
+ }
123
+ export function saveInitStatusStep(stepId, record, subId) {
124
+ const global = loadGlobalConfig();
125
+ const status = global.init_status ?? {};
126
+ if (subId) {
127
+ status[stepId] = {
128
+ ...status[stepId],
129
+ [subId]: record,
130
+ };
131
+ }
132
+ else {
133
+ status[stepId] = record;
134
+ }
135
+ saveInstanceConfig({ init_status: status }, true);
136
+ }
119
137
  export function expandHomeDir(p) {
120
138
  if (p.startsWith("~")) {
121
139
  return path.join(os.homedir(), p.slice(1));
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,128 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { validateDomain } from "../domain.js";
3
+ describe("validateDomain", () => {
4
+ describe("valid hostnames", () => {
5
+ it("accepts example.com", () => {
6
+ expect(validateDomain("example.com", false, false)).toEqual([]);
7
+ });
8
+ it("accepts subdomain", () => {
9
+ expect(validateDomain("mail.example.com", false, false)).toEqual([]);
10
+ });
11
+ it("accepts multi-level domain", () => {
12
+ expect(validateDomain("a.b.c.d.e.com", false, false)).toEqual([]);
13
+ });
14
+ it("accepts hyphen in middle of label", () => {
15
+ expect(validateDomain("my-domain.com", false, false)).toEqual([]);
16
+ });
17
+ it("accepts digits in labels", () => {
18
+ expect(validateDomain("example123.com", false, false)).toEqual([]);
19
+ });
20
+ it("accepts single-char labels", () => {
21
+ expect(validateDomain("a.b", false, false)).toEqual([]);
22
+ });
23
+ it("accepts 63-char label (boundary)", () => {
24
+ const label = "a".repeat(63);
25
+ expect(validateDomain(`${label}.com`, false, false)).toEqual([]);
26
+ });
27
+ });
28
+ describe("valid IP literals", () => {
29
+ it("accepts IPv4 literal", () => {
30
+ expect(validateDomain("192.168.1.1", true, false)).toEqual([]);
31
+ });
32
+ it("accepts IPv4 with boundary values", () => {
33
+ expect(validateDomain("0.0.0.0", true, false)).toEqual([]);
34
+ expect(validateDomain("255.255.255.255", true, false)).toEqual([]);
35
+ });
36
+ it("accepts IPv6 literal", () => {
37
+ expect(validateDomain("2001:db8::1", false, true)).toEqual([]);
38
+ });
39
+ it("accepts full IPv6", () => {
40
+ expect(validateDomain("2001:0db8:0000:0000:0000:0000:0000:0001", false, true)).toEqual([]);
41
+ });
42
+ it("accepts IPv6 loopback", () => {
43
+ expect(validateDomain("::1", false, true)).toEqual([]);
44
+ });
45
+ it("accepts IPv6 fe80", () => {
46
+ expect(validateDomain("fe80::1", false, true)).toEqual([]);
47
+ });
48
+ });
49
+ describe("hostname label errors", () => {
50
+ it("rejects label starting with hyphen", () => {
51
+ const errors = validateDomain("example.-domain.com", false, false);
52
+ expect(errors.some((e) => e.code === "ERR_DOMAIN_LABEL_HYPHEN_START")).toBe(true);
53
+ });
54
+ it("rejects label ending with hyphen", () => {
55
+ const errors = validateDomain("example.domain-.com", false, false);
56
+ expect(errors.some((e) => e.code === "ERR_DOMAIN_LABEL_HYPHEN_END")).toBe(true);
57
+ });
58
+ it("rejects empty label (consecutive dots)", () => {
59
+ const errors = validateDomain("example..com", false, false);
60
+ expect(errors.some((e) => e.code === "ERR_DOMAIN_LABEL_EMPTY")).toBe(true);
61
+ });
62
+ it("rejects label exceeding 63 chars", () => {
63
+ const label = "a".repeat(64);
64
+ const errors = validateDomain(`${label}.com`, false, false);
65
+ expect(errors.some((e) => e.code === "ERR_DOMAIN_LABEL_LENGTH")).toBe(true);
66
+ });
67
+ it("rejects invalid char in label", () => {
68
+ const errors = validateDomain("example.c_m.com", false, false);
69
+ expect(errors.some((e) => e.code === "ERR_DOMAIN_INVALID_CHAR")).toBe(true);
70
+ });
71
+ it("rejects domain with no dot", () => {
72
+ const errors = validateDomain("localhost", false, false);
73
+ expect(errors.some((e) => e.code === "ERR_DOMAIN_NO_DOT")).toBe(true);
74
+ });
75
+ it("rejects numeric TLD", () => {
76
+ const errors = validateDomain("example.123", false, false);
77
+ expect(errors.some((e) => e.code === "ERR_DOMAIN_NUMERIC_TLD")).toBe(true);
78
+ });
79
+ });
80
+ describe("length errors", () => {
81
+ it("rejects empty domain", () => {
82
+ const errors = validateDomain("", false, false);
83
+ expect(errors.some((e) => e.code === "ERR_DOMAIN_EMPTY")).toBe(true);
84
+ });
85
+ it("rejects domain exceeding 255 chars", () => {
86
+ const label = "a".repeat(63);
87
+ const domain = `${label}.${label}.${label}.${label}.${label}`;
88
+ // That's ~319 chars, > 255
89
+ const errors = validateDomain(domain, false, false, 255);
90
+ expect(errors.some((e) => e.code === "ERR_DOMAIN_LENGTH")).toBe(true);
91
+ });
92
+ it("accepts with custom max domain length", () => {
93
+ const domain = "a".repeat(10) + ".b";
94
+ const errors = validateDomain(domain, false, false, 100);
95
+ expect(errors.filter((e) => e.code === "ERR_DOMAIN_LENGTH")).toEqual([]);
96
+ });
97
+ });
98
+ describe("IPv4 literal errors", () => {
99
+ it("rejects invalid octet value (>255)", () => {
100
+ const errors = validateDomain("256.1.1.1", true, false);
101
+ expect(errors.some((e) => e.code === "ERR_IPV4_INVALID")).toBe(true);
102
+ });
103
+ it("rejects wrong number of octets", () => {
104
+ const errors = validateDomain("1.2.3", true, false);
105
+ expect(errors.some((e) => e.code === "ERR_IPV4_INVALID")).toBe(true);
106
+ });
107
+ it("rejects non-numeric octet", () => {
108
+ const errors = validateDomain("abc.1.1.1", true, false);
109
+ expect(errors.some((e) => e.code === "ERR_IPV4_INVALID")).toBe(true);
110
+ });
111
+ });
112
+ describe("IPv6 literal errors", () => {
113
+ it("rejects empty IPv6", () => {
114
+ const errors = validateDomain("", false, true);
115
+ // Empty domain is caught by the general empty check, which takes precedence
116
+ expect(errors.some((e) => e.code === "ERR_DOMAIN_EMPTY")).toBe(true);
117
+ });
118
+ it("rejects invalid hex chars", () => {
119
+ const errors = validateDomain("gggg::1", false, true);
120
+ expect(errors.some((e) => e.code === "ERR_IPV6_INVALID")).toBe(true);
121
+ });
122
+ it("rejects too many segments", () => {
123
+ const errors = validateDomain("1:2:3:4:5:6:7:8:9", false, true);
124
+ expect(errors.some((e) => e.code === "ERR_IPV6_INVALID")).toBe(true);
125
+ });
126
+ });
127
+ });
128
+ //# sourceMappingURL=domain.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain.test.js","sourceRoot":"","sources":["../../../src/email-validator/__tests__/domain.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { validateLocalPart } from "../local-part.js";
3
+ describe("validateLocalPart", () => {
4
+ describe("valid dot-atom", () => {
5
+ it("accepts simple alphanumeric", () => {
6
+ expect(validateLocalPart("user")).toEqual([]);
7
+ });
8
+ it("accepts single character", () => {
9
+ expect(validateLocalPart("a")).toEqual([]);
10
+ });
11
+ it("accepts all allowed special characters", () => {
12
+ expect(validateLocalPart("a!#$%&'*+-/=?^_`{|}~z")).toEqual([]);
13
+ });
14
+ it("accepts dot in middle", () => {
15
+ expect(validateLocalPart("first.last")).toEqual([]);
16
+ });
17
+ it("accepts multiple dots", () => {
18
+ expect(validateLocalPart("a.b.c")).toEqual([]);
19
+ });
20
+ it("accepts digits", () => {
21
+ expect(validateLocalPart("user123")).toEqual([]);
22
+ });
23
+ it("accepts plus sign (common alias)", () => {
24
+ expect(validateLocalPart("user+tag")).toEqual([]);
25
+ });
26
+ it("accepts 64-char local part", () => {
27
+ const lp = "a".repeat(64);
28
+ expect(validateLocalPart(lp)).toEqual([]);
29
+ });
30
+ });
31
+ describe("valid quoted strings", () => {
32
+ it("accepts quoted string with spaces", () => {
33
+ expect(validateLocalPart('"hello world"')).toEqual([]);
34
+ });
35
+ it("accepts quoted string with special chars", () => {
36
+ expect(validateLocalPart('"john@doe"')).toEqual([]);
37
+ });
38
+ it("accepts quoted string with escaped chars", () => {
39
+ expect(validateLocalPart('"escaped \\" quote"')).toEqual([]);
40
+ });
41
+ it("accepts empty quoted string", () => {
42
+ expect(validateLocalPart('""')).toEqual([]);
43
+ });
44
+ });
45
+ describe("dot-atom errors", () => {
46
+ it("rejects leading dot", () => {
47
+ const errors = validateLocalPart(".user");
48
+ expect(errors.some((e) => e.code === "ERR_LOCAL_PART_DOT_START")).toBe(true);
49
+ });
50
+ it("rejects trailing dot", () => {
51
+ const errors = validateLocalPart("user.");
52
+ expect(errors.some((e) => e.code === "ERR_LOCAL_PART_DOT_END")).toBe(true);
53
+ });
54
+ it("rejects consecutive dots", () => {
55
+ const errors = validateLocalPart("user..name");
56
+ expect(errors.some((e) => e.code === "ERR_LOCAL_PART_CONSECUTIVE_DOTS")).toBe(true);
57
+ });
58
+ it("rejects invalid char (space)", () => {
59
+ const errors = validateLocalPart("user name");
60
+ expect(errors.some((e) => e.code === "ERR_LOCAL_PART_INVALID_CHAR")).toBe(true);
61
+ });
62
+ it("rejects invalid char (angle bracket)", () => {
63
+ const errors = validateLocalPart("<user>");
64
+ expect(errors.some((e) => e.code === "ERR_LOCAL_PART_INVALID_CHAR")).toBe(true);
65
+ });
66
+ });
67
+ describe("length errors", () => {
68
+ it("rejects empty local part", () => {
69
+ const errors = validateLocalPart("");
70
+ expect(errors.some((e) => e.code === "ERR_LOCAL_PART_EMPTY")).toBe(true);
71
+ });
72
+ it("rejects local part exceeding default 64 chars", () => {
73
+ const lp = "a".repeat(65);
74
+ const errors = validateLocalPart(lp);
75
+ expect(errors.some((e) => e.code === "ERR_LOCAL_PART_LENGTH")).toBe(true);
76
+ });
77
+ it("accepts with custom max length", () => {
78
+ const lp = "a".repeat(10);
79
+ const errors = validateLocalPart(lp, 10);
80
+ expect(errors).toEqual([]);
81
+ });
82
+ it("rejects with custom max length exceeded", () => {
83
+ const lp = "a".repeat(11);
84
+ const errors = validateLocalPart(lp, 10);
85
+ expect(errors.some((e) => e.code === "ERR_LOCAL_PART_LENGTH")).toBe(true);
86
+ });
87
+ });
88
+ describe("quoted string errors", () => {
89
+ it("rejects unbalanced quotes", () => {
90
+ const errors = validateLocalPart('"unbalanced');
91
+ // Falls through to dot-atom validation since it doesn't start+end with quotes
92
+ expect(errors.some((e) => e.code === "ERR_LOCAL_PART_INVALID_CHAR")).toBe(true);
93
+ });
94
+ it("rejects quote in middle of quoted string", () => {
95
+ const errors = validateLocalPart('"hello"world"');
96
+ expect(errors.some((e) => e.code === "ERR_UNBALANCED_QUOTES")).toBe(true);
97
+ });
98
+ });
99
+ describe("edge cases", () => {
100
+ it("multiple errors for dot-only local part", () => {
101
+ const errors = validateLocalPart(".");
102
+ expect(errors.length).toBeGreaterThanOrEqual(1);
103
+ });
104
+ it("multiple errors for double-dot-only", () => {
105
+ const errors = validateLocalPart("..");
106
+ expect(errors.length).toBeGreaterThanOrEqual(1);
107
+ });
108
+ });
109
+ });
110
+ //# sourceMappingURL=local-part.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-part.test.js","sourceRoot":"","sources":["../../../src/email-validator/__tests__/local-part.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,161 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { parseEmail } from "../parser.js";
3
+ const defaults = { allowDisplayName: true, allowComments: false };
4
+ describe("parseEmail", () => {
5
+ describe("simple addr-spec", () => {
6
+ it("parses user@domain.com", () => {
7
+ const r = parseEmail("user@domain.com", defaults);
8
+ expect(r.ok).toBe(true);
9
+ if (r.ok) {
10
+ expect(r.value.localPart).toBe("user");
11
+ expect(r.value.domain).toBe("domain.com");
12
+ expect(r.value.displayName).toBeNull();
13
+ expect(r.value.isIpLiteral).toBe(false);
14
+ expect(r.value.isIPv6).toBe(false);
15
+ }
16
+ });
17
+ it("parses a@b.c (minimal)", () => {
18
+ const r = parseEmail("a@b.c", defaults);
19
+ expect(r.ok).toBe(true);
20
+ if (r.ok) {
21
+ expect(r.value.localPart).toBe("a");
22
+ expect(r.value.domain).toBe("b.c");
23
+ }
24
+ });
25
+ it("parses addr-spec with subdomain", () => {
26
+ const r = parseEmail("user@mail.example.co.uk", defaults);
27
+ expect(r.ok).toBe(true);
28
+ if (r.ok) {
29
+ expect(r.value.localPart).toBe("user");
30
+ expect(r.value.domain).toBe("mail.example.co.uk");
31
+ }
32
+ });
33
+ });
34
+ describe("display name", () => {
35
+ it("extracts display name from angle-bracket form", () => {
36
+ const r = parseEmail("John Doe <john@example.com>", defaults);
37
+ expect(r.ok).toBe(true);
38
+ if (r.ok) {
39
+ expect(r.value.localPart).toBe("john");
40
+ expect(r.value.domain).toBe("example.com");
41
+ expect(r.value.displayName).toBe("John Doe");
42
+ }
43
+ });
44
+ it("returns null display name when not present", () => {
45
+ const r = parseEmail("user@domain.com", defaults);
46
+ expect(r.ok).toBe(true);
47
+ if (r.ok) {
48
+ expect(r.value.displayName).toBeNull();
49
+ }
50
+ });
51
+ it("ignores display name when disabled", () => {
52
+ const r = parseEmail("John <john@example.com>", { allowDisplayName: false });
53
+ expect(r.ok).toBe(true);
54
+ if (r.ok) {
55
+ expect(r.value.localPart).toBe("John <john");
56
+ expect(r.value.domain).toBe("example.com>");
57
+ }
58
+ });
59
+ });
60
+ describe("comments", () => {
61
+ it("strips comments when enabled", () => {
62
+ const r = parseEmail("user(comment)@domain.com", { allowComments: true });
63
+ expect(r.ok).toBe(true);
64
+ if (r.ok) {
65
+ expect(r.value.localPart).toBe("user");
66
+ expect(r.value.domain).toBe("domain.com");
67
+ }
68
+ });
69
+ it("rejects comments when disabled", () => {
70
+ const r = parseEmail("user(comment)@domain.com", { allowComments: false });
71
+ expect(r.ok).toBe(false);
72
+ if (!r.ok) {
73
+ expect(r.error.code).toBe("ERR_COMMENTS_NOT_ALLOWED");
74
+ }
75
+ });
76
+ it("strips multiple comments", () => {
77
+ const r = parseEmail("(hello)user(world)@domain.com", { allowComments: true });
78
+ expect(r.ok).toBe(true);
79
+ if (r.ok) {
80
+ expect(r.value.localPart).toBe("user");
81
+ }
82
+ });
83
+ it("strips nested comments", () => {
84
+ const r = parseEmail("user((nested))@domain.com", { allowComments: true });
85
+ expect(r.ok).toBe(true);
86
+ if (r.ok) {
87
+ expect(r.value.localPart).toBe("user");
88
+ }
89
+ });
90
+ });
91
+ describe("IP literals", () => {
92
+ it("detects IPv4 literal", () => {
93
+ const r = parseEmail("user@[192.168.1.1]", defaults);
94
+ expect(r.ok).toBe(true);
95
+ if (r.ok) {
96
+ expect(r.value.domain).toBe("192.168.1.1");
97
+ expect(r.value.isIpLiteral).toBe(true);
98
+ expect(r.value.isIPv6).toBe(false);
99
+ }
100
+ });
101
+ it("detects IPv6 literal", () => {
102
+ const r = parseEmail("user@[IPv6:2001:db8::1]", defaults);
103
+ expect(r.ok).toBe(true);
104
+ if (r.ok) {
105
+ expect(r.value.domain).toBe("2001:db8::1");
106
+ expect(r.value.isIpLiteral).toBe(true);
107
+ expect(r.value.isIPv6).toBe(true);
108
+ }
109
+ });
110
+ it("detects IPv6 literal case-insensitively", () => {
111
+ const r = parseEmail("user@[ipv6:fe80::1]", defaults);
112
+ expect(r.ok).toBe(true);
113
+ if (r.ok) {
114
+ expect(r.value.isIPv6).toBe(true);
115
+ expect(r.value.domain).toBe("fe80::1");
116
+ }
117
+ });
118
+ });
119
+ describe("error cases", () => {
120
+ it("ERR_EMPTY_INPUT for empty string", () => {
121
+ const r = parseEmail("", defaults);
122
+ expect(r.ok).toBe(false);
123
+ if (!r.ok)
124
+ expect(r.error.code).toBe("ERR_EMPTY_INPUT");
125
+ });
126
+ it("ERR_EMPTY_INPUT for whitespace-only", () => {
127
+ const r = parseEmail(" ", defaults);
128
+ expect(r.ok).toBe(false);
129
+ if (!r.ok)
130
+ expect(r.error.code).toBe("ERR_EMPTY_INPUT");
131
+ });
132
+ it("ERR_TOTAL_LENGTH for exceeding max", () => {
133
+ const long = "a".repeat(255) + "@b.c";
134
+ const r = parseEmail(long, { maxTotalLength: 254 });
135
+ expect(r.ok).toBe(false);
136
+ if (!r.ok)
137
+ expect(r.error.code).toBe("ERR_TOTAL_LENGTH");
138
+ });
139
+ it("ERR_NO_AT_SIGN when missing @", () => {
140
+ const r = parseEmail("not-an-email", defaults);
141
+ expect(r.ok).toBe(false);
142
+ if (!r.ok)
143
+ expect(r.error.code).toBe("ERR_NO_AT_SIGN");
144
+ });
145
+ it("ERR_MULTIPLE_AT for double @", () => {
146
+ const r = parseEmail("a@b@c.com", defaults);
147
+ expect(r.ok).toBe(false);
148
+ if (!r.ok)
149
+ expect(r.error.code).toBe("ERR_MULTIPLE_AT");
150
+ });
151
+ it("trims whitespace around input", () => {
152
+ const r = parseEmail(" user@domain.com ", defaults);
153
+ expect(r.ok).toBe(true);
154
+ if (r.ok) {
155
+ expect(r.value.localPart).toBe("user");
156
+ expect(r.value.domain).toBe("domain.com");
157
+ }
158
+ });
159
+ });
160
+ });
161
+ //# sourceMappingURL=parser.test.js.map