dk-domain-agent-core 0.1.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.
Files changed (54) hide show
  1. package/README.md +5 -0
  2. package/dist/availability.d.ts +3 -0
  3. package/dist/availability.d.ts.map +1 -0
  4. package/dist/availability.js +75 -0
  5. package/dist/availability.js.map +1 -0
  6. package/dist/clock.d.ts +3 -0
  7. package/dist/clock.d.ts.map +1 -0
  8. package/dist/clock.js +5 -0
  9. package/dist/clock.js.map +1 -0
  10. package/dist/file-cache.d.ts +17 -0
  11. package/dist/file-cache.d.ts.map +1 -0
  12. package/dist/file-cache.js +74 -0
  13. package/dist/file-cache.js.map +1 -0
  14. package/dist/file-lock.d.ts +9 -0
  15. package/dist/file-lock.d.ts.map +1 -0
  16. package/dist/file-lock.js +56 -0
  17. package/dist/file-lock.js.map +1 -0
  18. package/dist/file-rate-limiter.d.ts +14 -0
  19. package/dist/file-rate-limiter.d.ts.map +1 -0
  20. package/dist/file-rate-limiter.js +82 -0
  21. package/dist/file-rate-limiter.js.map +1 -0
  22. package/dist/index.d.ts +12 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +11 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/memory-store.d.ts +12 -0
  27. package/dist/memory-store.d.ts.map +1 -0
  28. package/dist/memory-store.js +47 -0
  29. package/dist/memory-store.js.map +1 -0
  30. package/dist/normalize.d.ts +3 -0
  31. package/dist/normalize.d.ts.map +1 -0
  32. package/dist/normalize.js +39 -0
  33. package/dist/normalize.js.map +1 -0
  34. package/dist/parser.d.ts +3 -0
  35. package/dist/parser.d.ts.map +1 -0
  36. package/dist/parser.js +47 -0
  37. package/dist/parser.js.map +1 -0
  38. package/dist/result.d.ts +4 -0
  39. package/dist/result.d.ts.map +1 -0
  40. package/dist/result.js +36 -0
  41. package/dist/result.js.map +1 -0
  42. package/dist/ttl.d.ts +3 -0
  43. package/dist/ttl.d.ts.map +1 -0
  44. package/dist/ttl.js +15 -0
  45. package/dist/ttl.js.map +1 -0
  46. package/dist/types.d.ts +62 -0
  47. package/dist/types.d.ts.map +1 -0
  48. package/dist/types.js +2 -0
  49. package/dist/types.js.map +1 -0
  50. package/dist/whois-client.d.ts +8 -0
  51. package/dist/whois-client.d.ts.map +1 -0
  52. package/dist/whois-client.js +36 -0
  53. package/dist/whois-client.js.map +1 -0
  54. package/package.json +33 -0
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # dk-domain-agent-core
2
+
3
+ Core library for WHOIS-inferred `.dk` domain availability checks.
4
+
5
+ Most users should install `dk-domain-agent` for the CLI or `dk-domain-agent-mcp` for AI agents.
@@ -0,0 +1,3 @@
1
+ import type { AvailabilityOptions, AvailabilityResult } from "./types.js";
2
+ export declare function checkDkDomainAvailability(input: string, options?: AvailabilityOptions): Promise<AvailabilityResult>;
3
+ //# sourceMappingURL=availability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"availability.d.ts","sourceRoot":"","sources":["../src/availability.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAoB,MAAM,YAAY,CAAC;AAM5F,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC,CAmE7B"}
@@ -0,0 +1,75 @@
1
+ import { systemClock } from "./clock.js";
2
+ import { FileAvailabilityCache } from "./file-cache.js";
3
+ import { FileRateLimiter } from "./file-rate-limiter.js";
4
+ import { normalizeDkDomain } from "./normalize.js";
5
+ import { parsePunktumWhois } from "./parser.js";
6
+ import { availabilityResult, invalidResult } from "./result.js";
7
+ import { ttlForStatus } from "./ttl.js";
8
+ import { PunktumWhoisClient } from "./whois-client.js";
9
+ const DEFAULT_TIMEOUT_MS = 8000;
10
+ const DEFAULT_QUEUE_WAIT_MS = 10_000;
11
+ const PUNKTUM_WHOIS_INTERVAL_MS = 1000;
12
+ export async function checkDkDomainAvailability(input, options = {}) {
13
+ const clock = options.clock ?? systemClock;
14
+ const checkedAt = new Date(clock.now()).toISOString();
15
+ let normalized;
16
+ try {
17
+ normalized = normalizeDkDomain(input);
18
+ }
19
+ catch {
20
+ return invalidResult(input, checkedAt);
21
+ }
22
+ const cache = options.cache ?? new FileAvailabilityCache({ clock });
23
+ if (!options.bypassCache) {
24
+ const nowMs = clock.now();
25
+ const cached = await cache.get(normalized.asciiDomain, nowMs);
26
+ if (cached) {
27
+ return {
28
+ ...cached.result,
29
+ cache: {
30
+ hit: true,
31
+ ttlSeconds: Math.max(0, Math.ceil((cached.expiresAtMs - nowMs) / 1000))
32
+ }
33
+ };
34
+ }
35
+ }
36
+ const rateLimiter = options.rateLimiter ?? new FileRateLimiter({ clock });
37
+ const turn = await rateLimiter.waitForTurn({
38
+ intervalMs: PUNKTUM_WHOIS_INTERVAL_MS,
39
+ maxWaitMs: options.maxQueueWaitMs ?? DEFAULT_QUEUE_WAIT_MS,
40
+ clock
41
+ });
42
+ if (!turn.acquired) {
43
+ return availabilityResult(normalized.unicodeDomain, normalized.asciiDomain, {
44
+ available: null,
45
+ status: "rate_limited",
46
+ confidence: "unknown"
47
+ }, new Date(clock.now()).toISOString(), false, null);
48
+ }
49
+ const whoisClient = options.whoisClient ?? new PunktumWhoisClient();
50
+ const parsed = await queryAndParse(normalized.asciiDomain, whoisClient, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
51
+ const result = availabilityResult(normalized.unicodeDomain, normalized.asciiDomain, parsed, new Date(clock.now()).toISOString(), false, null);
52
+ const ttlSeconds = ttlForStatus(result.status);
53
+ await cache.set(normalized.asciiDomain, result, ttlSeconds, clock.now());
54
+ return {
55
+ ...result,
56
+ cache: {
57
+ hit: false,
58
+ ttlSeconds
59
+ }
60
+ };
61
+ }
62
+ async function queryAndParse(domain, whoisClient, timeoutMs) {
63
+ try {
64
+ const raw = await whoisClient.query(domain, timeoutMs);
65
+ return parsePunktumWhois(raw);
66
+ }
67
+ catch {
68
+ return {
69
+ available: null,
70
+ status: "unknown",
71
+ confidence: "unknown"
72
+ };
73
+ }
74
+ }
75
+ //# sourceMappingURL=availability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"availability.js","sourceRoot":"","sources":["../src/availability.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Clock } from "./types.js";
2
+ export declare const systemClock: Clock;
3
+ //# sourceMappingURL=clock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clock.d.ts","sourceRoot":"","sources":["../src/clock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,eAAO,MAAM,WAAW,EAAE,KAGzB,CAAC"}
package/dist/clock.js ADDED
@@ -0,0 +1,5 @@
1
+ export const systemClock = {
2
+ now: () => Date.now(),
3
+ sleep: (ms) => new Promise(resolve => setTimeout(resolve, ms))
4
+ };
5
+ //# sourceMappingURL=clock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clock.js","sourceRoot":"","sources":["../src/clock.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,WAAW,GAAU;IAChC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;IACrB,KAAK,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;CACvE,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { AvailabilityCache, AvailabilityResult, CachedAvailability, Clock } from "./types.js";
2
+ export declare function defaultStateDir(): string;
3
+ export declare class FileAvailabilityCache implements AvailabilityCache {
4
+ private readonly stateFile;
5
+ private readonly lockDir;
6
+ private readonly clock;
7
+ constructor(options?: {
8
+ stateFile?: string;
9
+ clock?: Clock;
10
+ });
11
+ get(domain: string, nowMs: number): Promise<CachedAvailability | null>;
12
+ set(domain: string, result: AvailabilityResult, ttlSeconds: number, nowMs: number): Promise<void>;
13
+ clear(): Promise<void>;
14
+ private readState;
15
+ private writeState;
16
+ }
17
+ //# sourceMappingURL=file-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-cache.d.ts","sourceRoot":"","sources":["../src/file-cache.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAOnG,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,qBAAa,qBAAsB,YAAW,iBAAiB;IAC7D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;gBAElB,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAO;IASzD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAatE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAajG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAMd,SAAS;YAgBT,UAAU;CAMzB"}
@@ -0,0 +1,74 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { systemClock } from "./clock.js";
5
+ import { withFileLock } from "./file-lock.js";
6
+ export function defaultStateDir() {
7
+ return process.env.DK_DOMAIN_AGENT_STATE_DIR ?? path.join(os.homedir(), ".dk-domain-agent");
8
+ }
9
+ export class FileAvailabilityCache {
10
+ stateFile;
11
+ lockDir;
12
+ clock;
13
+ constructor(options = {}) {
14
+ this.stateFile =
15
+ options.stateFile ??
16
+ process.env.DK_DOMAIN_AGENT_CACHE_FILE ??
17
+ path.join(defaultStateDir(), "cache.json");
18
+ this.lockDir = `${this.stateFile}.lock`;
19
+ this.clock = options.clock ?? systemClock;
20
+ }
21
+ async get(domain, nowMs) {
22
+ return withFileLock(this.lockDir, { clock: this.clock }, async () => {
23
+ const state = await this.readState();
24
+ const cached = state.entries[domain];
25
+ if (!cached || cached.expiresAtMs <= nowMs) {
26
+ return null;
27
+ }
28
+ return cached;
29
+ });
30
+ }
31
+ async set(domain, result, ttlSeconds, nowMs) {
32
+ if (ttlSeconds <= 0)
33
+ return;
34
+ await withFileLock(this.lockDir, { clock: this.clock }, async () => {
35
+ const state = await this.readState();
36
+ state.entries[domain] = {
37
+ result,
38
+ expiresAtMs: nowMs + ttlSeconds * 1000
39
+ };
40
+ await this.writeState(state);
41
+ });
42
+ }
43
+ async clear() {
44
+ await withFileLock(this.lockDir, { clock: this.clock }, async () => {
45
+ await this.writeState({ version: 1, entries: {} });
46
+ });
47
+ }
48
+ async readState() {
49
+ try {
50
+ const raw = await fs.readFile(this.stateFile, "utf8");
51
+ const parsed = JSON.parse(raw);
52
+ return {
53
+ version: 1,
54
+ entries: parsed.entries ?? {}
55
+ };
56
+ }
57
+ catch (error) {
58
+ if (isMissingFile(error)) {
59
+ return { version: 1, entries: {} };
60
+ }
61
+ throw error;
62
+ }
63
+ }
64
+ async writeState(state) {
65
+ await fs.mkdir(path.dirname(this.stateFile), { recursive: true });
66
+ const tmpFile = `${this.stateFile}.${process.pid}.tmp`;
67
+ await fs.writeFile(tmpFile, JSON.stringify(state, null, 2));
68
+ await fs.rename(tmpFile, this.stateFile);
69
+ }
70
+ }
71
+ function isMissingFile(error) {
72
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
73
+ }
74
+ //# sourceMappingURL=file-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-cache.js","sourceRoot":"","sources":["../src/file-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Clock } from "./types.js";
2
+ export type FileLockOptions = {
3
+ staleMs?: number;
4
+ timeoutMs?: number;
5
+ retryMs?: number;
6
+ clock: Clock;
7
+ };
8
+ export declare function withFileLock<T>(lockDir: string, options: FileLockOptions, task: () => Promise<T>): Promise<T>;
9
+ //# sourceMappingURL=file-lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-lock.d.ts","sourceRoot":"","sources":["../src/file-lock.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AAMF,wBAAsB,YAAY,CAAC,CAAC,EAClC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACrB,OAAO,CAAC,CAAC,CAAC,CAkCZ"}
@@ -0,0 +1,56 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ const DEFAULT_STALE_MS = 30_000;
4
+ const DEFAULT_TIMEOUT_MS = 5_000;
5
+ const DEFAULT_RETRY_MS = 25;
6
+ export async function withFileLock(lockDir, options, task) {
7
+ const staleMs = options.staleMs ?? DEFAULT_STALE_MS;
8
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
9
+ const retryMs = options.retryMs ?? DEFAULT_RETRY_MS;
10
+ const startedAt = options.clock.now();
11
+ await fs.mkdir(path.dirname(lockDir), { recursive: true });
12
+ while (true) {
13
+ try {
14
+ await fs.mkdir(lockDir);
15
+ break;
16
+ }
17
+ catch (error) {
18
+ if (!isErrnoException(error) || error.code !== "EEXIST") {
19
+ throw error;
20
+ }
21
+ if (await removeIfStale(lockDir, staleMs, options.clock.now())) {
22
+ continue;
23
+ }
24
+ if (options.clock.now() - startedAt >= timeoutMs) {
25
+ throw new Error(`Timed out waiting for lock: ${lockDir}`);
26
+ }
27
+ await options.clock.sleep(retryMs);
28
+ }
29
+ }
30
+ try {
31
+ return await task();
32
+ }
33
+ finally {
34
+ await fs.rm(lockDir, { recursive: true, force: true });
35
+ }
36
+ }
37
+ async function removeIfStale(lockDir, staleMs, nowMs) {
38
+ try {
39
+ const stats = await fs.stat(lockDir);
40
+ if (nowMs - stats.mtimeMs < staleMs) {
41
+ return false;
42
+ }
43
+ await fs.rm(lockDir, { recursive: true, force: true });
44
+ return true;
45
+ }
46
+ catch (error) {
47
+ if (isErrnoException(error) && error.code === "ENOENT") {
48
+ return true;
49
+ }
50
+ throw error;
51
+ }
52
+ }
53
+ function isErrnoException(error) {
54
+ return error instanceof Error && "code" in error;
55
+ }
56
+ //# sourceMappingURL=file-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-lock.js","sourceRoot":"","sources":["../src/file-lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAU7B,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAChC,MAAM,kBAAkB,GAAG,KAAK,CAAC;AACjC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAE5B,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,OAAwB,EACxB,IAAsB;IAEtB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,gBAAgB,CAAC;IACpD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,kBAAkB,CAAC;IAC1D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,gBAAgB,CAAC;IACpD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;IAEtC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3D,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACxB,MAAM;QACR,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACxD,MAAM,KAAK,CAAC;YACd,CAAC;YAED,IAAI,MAAM,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;gBAC/D,SAAS;YACX,CAAC;YAED,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,SAAS,IAAI,SAAS,EAAE,CAAC;gBACjD,MAAM,IAAI,KAAK,CAAC,+BAA+B,OAAO,EAAE,CAAC,CAAC;YAC5D,CAAC;YAED,MAAM,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,EAAE,CAAC;IACtB,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,OAAe,EAAE,OAAe,EAAE,KAAa;IAC1E,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC;YACpC,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,gBAAgB,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAc;IACtC,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,CAAC;AACnD,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { Clock, RateLimitRequest, RateLimitResult, RateLimiter } from "./types.js";
2
+ export declare class FileRateLimiter implements RateLimiter {
3
+ private readonly stateFile;
4
+ private readonly lockDir;
5
+ private readonly clock;
6
+ constructor(options?: {
7
+ stateFile?: string;
8
+ clock?: Clock;
9
+ });
10
+ waitForTurn(request: RateLimitRequest): Promise<RateLimitResult>;
11
+ private readState;
12
+ private writeState;
13
+ }
14
+ //# sourceMappingURL=file-rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-rate-limiter.d.ts","sourceRoot":"","sources":["../src/file-rate-limiter.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAOxF,qBAAa,eAAgB,YAAW,WAAW;IACjD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;gBAElB,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAO;IASzD,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;YA8CxD,SAAS;YAgBT,UAAU;CAMzB"}
@@ -0,0 +1,82 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { systemClock } from "./clock.js";
4
+ import { defaultStateDir } from "./file-cache.js";
5
+ import { withFileLock } from "./file-lock.js";
6
+ export class FileRateLimiter {
7
+ stateFile;
8
+ lockDir;
9
+ clock;
10
+ constructor(options = {}) {
11
+ this.stateFile =
12
+ options.stateFile ??
13
+ process.env.DK_DOMAIN_AGENT_RATE_LIMIT_FILE ??
14
+ path.join(defaultStateDir(), "rate-limit.json");
15
+ this.lockDir = `${this.stateFile}.lock`;
16
+ this.clock = options.clock ?? systemClock;
17
+ }
18
+ async waitForTurn(request) {
19
+ const clock = request.clock ?? this.clock;
20
+ const startedAt = clock.now();
21
+ while (true) {
22
+ const acquired = await withFileLock(this.lockDir, { clock }, async () => {
23
+ const state = await this.readState();
24
+ const now = clock.now();
25
+ if (state.nextAllowedMs > now) {
26
+ return {
27
+ acquired: false,
28
+ waitMs: state.nextAllowedMs - now
29
+ };
30
+ }
31
+ await this.writeState({
32
+ version: 1,
33
+ nextAllowedMs: now + request.intervalMs
34
+ });
35
+ return {
36
+ acquired: true,
37
+ waitMs: 0
38
+ };
39
+ });
40
+ if (acquired.acquired) {
41
+ return {
42
+ acquired: true,
43
+ waitedMs: clock.now() - startedAt
44
+ };
45
+ }
46
+ const elapsedMs = clock.now() - startedAt;
47
+ if (elapsedMs + acquired.waitMs > request.maxWaitMs) {
48
+ return {
49
+ acquired: false,
50
+ waitedMs: elapsedMs
51
+ };
52
+ }
53
+ await clock.sleep(acquired.waitMs);
54
+ }
55
+ }
56
+ async readState() {
57
+ try {
58
+ const raw = await fs.readFile(this.stateFile, "utf8");
59
+ const parsed = JSON.parse(raw);
60
+ return {
61
+ version: 1,
62
+ nextAllowedMs: parsed.nextAllowedMs ?? 0
63
+ };
64
+ }
65
+ catch (error) {
66
+ if (isMissingFile(error)) {
67
+ return { version: 1, nextAllowedMs: 0 };
68
+ }
69
+ throw error;
70
+ }
71
+ }
72
+ async writeState(state) {
73
+ await fs.mkdir(path.dirname(this.stateFile), { recursive: true });
74
+ const tmpFile = `${this.stateFile}.${process.pid}.tmp`;
75
+ await fs.writeFile(tmpFile, JSON.stringify(state, null, 2));
76
+ await fs.rename(tmpFile, this.stateFile);
77
+ }
78
+ }
79
+ function isMissingFile(error) {
80
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
81
+ }
82
+ //# sourceMappingURL=file-rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-rate-limiter.js","sourceRoot":"","sources":["../src/file-rate-limiter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export { checkDkDomainAvailability } from "./availability.js";
2
+ export { systemClock } from "./clock.js";
3
+ export { FileAvailabilityCache, defaultStateDir } from "./file-cache.js";
4
+ export { FileRateLimiter } from "./file-rate-limiter.js";
5
+ export { MemoryAvailabilityCache, MemoryRateLimiter } from "./memory-store.js";
6
+ export { normalizeDkDomain } from "./normalize.js";
7
+ export { parsePunktumWhois } from "./parser.js";
8
+ export { availabilityResult, invalidResult } from "./result.js";
9
+ export { ttlForStatus } from "./ttl.js";
10
+ export { PunktumWhoisClient } from "./whois-client.js";
11
+ export type { AvailabilityCache, AvailabilityConfidence, AvailabilityOptions, AvailabilityResult, AvailabilityStatus, CachedAvailability, Clock, NormalizedDomain, RateLimiter, RateLimitRequest, RateLimitResult, WhoisClient, WhoisParseResult } from "./types.js";
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EACV,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,EACL,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,gBAAgB,EACjB,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ export { checkDkDomainAvailability } from "./availability.js";
2
+ export { systemClock } from "./clock.js";
3
+ export { FileAvailabilityCache, defaultStateDir } from "./file-cache.js";
4
+ export { FileRateLimiter } from "./file-rate-limiter.js";
5
+ export { MemoryAvailabilityCache, MemoryRateLimiter } from "./memory-store.js";
6
+ export { normalizeDkDomain } from "./normalize.js";
7
+ export { parsePunktumWhois } from "./parser.js";
8
+ export { availabilityResult, invalidResult } from "./result.js";
9
+ export { ttlForStatus } from "./ttl.js";
10
+ export { PunktumWhoisClient } from "./whois-client.js";
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { AvailabilityCache, AvailabilityResult, CachedAvailability, RateLimitRequest, RateLimitResult, RateLimiter } from "./types.js";
2
+ export declare class MemoryAvailabilityCache implements AvailabilityCache {
3
+ private readonly cache;
4
+ get(domain: string, nowMs: number): Promise<CachedAvailability | null>;
5
+ set(domain: string, result: AvailabilityResult, ttlSeconds: number, nowMs: number): Promise<void>;
6
+ clear(): Promise<void>;
7
+ }
8
+ export declare class MemoryRateLimiter implements RateLimiter {
9
+ private nextAllowedMs;
10
+ waitForTurn(request: RateLimitRequest): Promise<RateLimitResult>;
11
+ }
12
+ //# sourceMappingURL=memory-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../src/memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB,qBAAa,uBAAwB,YAAW,iBAAiB;IAC/D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyC;IAEzD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAQtE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQjG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B;AAED,qBAAa,iBAAkB,YAAW,WAAW;IACnD,OAAO,CAAC,aAAa,CAAK;IAEpB,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;CA2BvE"}
@@ -0,0 +1,47 @@
1
+ export class MemoryAvailabilityCache {
2
+ cache = new Map();
3
+ async get(domain, nowMs) {
4
+ const cached = this.cache.get(domain);
5
+ if (!cached || cached.expiresAtMs <= nowMs) {
6
+ return null;
7
+ }
8
+ return cached;
9
+ }
10
+ async set(domain, result, ttlSeconds, nowMs) {
11
+ if (ttlSeconds <= 0)
12
+ return;
13
+ this.cache.set(domain, {
14
+ result,
15
+ expiresAtMs: nowMs + ttlSeconds * 1000
16
+ });
17
+ }
18
+ async clear() {
19
+ this.cache.clear();
20
+ }
21
+ }
22
+ export class MemoryRateLimiter {
23
+ nextAllowedMs = 0;
24
+ async waitForTurn(request) {
25
+ const startedAt = request.clock.now();
26
+ while (true) {
27
+ const now = request.clock.now();
28
+ if (this.nextAllowedMs <= now) {
29
+ this.nextAllowedMs = now + request.intervalMs;
30
+ return {
31
+ acquired: true,
32
+ waitedMs: now - startedAt
33
+ };
34
+ }
35
+ const waitMs = this.nextAllowedMs - now;
36
+ const elapsedMs = now - startedAt;
37
+ if (elapsedMs + waitMs > request.maxWaitMs) {
38
+ return {
39
+ acquired: false,
40
+ waitedMs: elapsedMs
41
+ };
42
+ }
43
+ await request.clock.sleep(waitMs);
44
+ }
45
+ }
46
+ }
47
+ //# sourceMappingURL=memory-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-store.js","sourceRoot":"","sources":["../src/memory-store.ts"],"names":[],"mappings":"AASA,MAAM,OAAO,uBAAuB;IACjB,KAAK,GAAG,IAAI,GAAG,EAA8B,CAAC;IAE/D,KAAK,CAAC,GAAG,CAAC,MAAc,EAAE,KAAa;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,WAAW,IAAI,KAAK,EAAE,CAAC;YAC3C,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,MAAc,EAAE,MAA0B,EAAE,UAAkB,EAAE,KAAa;QACrF,IAAI,UAAU,IAAI,CAAC;YAAE,OAAO;QAC5B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE;YACrB,MAAM;YACN,WAAW,EAAE,KAAK,GAAG,UAAU,GAAG,IAAI;SACvC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF;AAED,MAAM,OAAO,iBAAiB;IACpB,aAAa,GAAG,CAAC,CAAC;IAE1B,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QAEtC,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YAEhC,IAAI,IAAI,CAAC,aAAa,IAAI,GAAG,EAAE,CAAC;gBAC9B,IAAI,CAAC,aAAa,GAAG,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC;gBAC9C,OAAO;oBACL,QAAQ,EAAE,IAAI;oBACd,QAAQ,EAAE,GAAG,GAAG,SAAS;iBAC1B,CAAC;YACJ,CAAC;YAED,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,GAAG,GAAG,CAAC;YACxC,MAAM,SAAS,GAAG,GAAG,GAAG,SAAS,CAAC;YAElC,IAAI,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;gBAC3C,OAAO;oBACL,QAAQ,EAAE,KAAK;oBACf,QAAQ,EAAE,SAAS;iBACpB,CAAC;YACJ,CAAC;YAED,MAAM,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ import type { NormalizedDomain } from "./types.js";
2
+ export declare function normalizeDkDomain(input: string): NormalizedDomain;
3
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAKnD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CA6BjE"}
@@ -0,0 +1,39 @@
1
+ import { domainToASCII, domainToUnicode } from "node:url";
2
+ const DK_SUFFIX = ".dk";
3
+ const LABEL_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;
4
+ export function normalizeDkDomain(input) {
5
+ const trimmed = input.trim().replace(/\.$/, "");
6
+ if (!trimmed) {
7
+ throw new Error("Domain is required.");
8
+ }
9
+ if (trimmed.includes(".") && !trimmed.toLowerCase().endsWith(DK_SUFFIX)) {
10
+ throw new Error("Only .dk domains are supported.");
11
+ }
12
+ const candidate = trimmed.toLowerCase().endsWith(DK_SUFFIX)
13
+ ? trimmed.toLowerCase()
14
+ : `${trimmed.toLowerCase()}${DK_SUFFIX}`;
15
+ const asciiDomain = domainToASCII(candidate);
16
+ const unicodeDomain = domainToUnicode(asciiDomain);
17
+ if (!asciiDomain || !asciiDomain.endsWith(DK_SUFFIX)) {
18
+ throw new Error("Only .dk domains are supported.");
19
+ }
20
+ validateAsciiDomain(asciiDomain);
21
+ return {
22
+ input,
23
+ asciiDomain,
24
+ unicodeDomain
25
+ };
26
+ }
27
+ function validateAsciiDomain(domain) {
28
+ if (domain.length > 253) {
29
+ throw new Error("Domain is too long.");
30
+ }
31
+ const labels = domain.split(".");
32
+ if (labels.length !== 2 || labels[1] !== "dk") {
33
+ throw new Error("Only second-level .dk domains are supported.");
34
+ }
35
+ if (!LABEL_PATTERN.test(labels[0])) {
36
+ throw new Error("Invalid .dk domain label.");
37
+ }
38
+ }
39
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAG1D,MAAM,SAAS,GAAG,KAAK,CAAC;AACxB,MAAM,aAAa,GAAG,yCAAyC,CAAC;AAEhE,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAEhD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC;QACzD,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE;QACvB,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,EAAE,GAAG,SAAS,EAAE,CAAC;IAE3C,MAAM,WAAW,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IAC7C,MAAM,aAAa,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IAEnD,IAAI,CAAC,WAAW,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IAED,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAEjC,OAAO;QACL,KAAK;QACL,WAAW;QACX,aAAa;KACd,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAc;IACzC,IAAI,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACzC,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAEjC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IAED,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { WhoisParseResult } from "./types.js";
2
+ export declare function parsePunktumWhois(raw: string): WhoisParseResult;
3
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAOnD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAwC/D"}
package/dist/parser.js ADDED
@@ -0,0 +1,47 @@
1
+ const NOT_FOUND = /No entries found for the selected source\./i;
2
+ const DOMAIN_FIELD = /^Domain:\s+/im;
3
+ const WAITING_LIST = /(waiting list|offered to waiting list|Status:\s*Offered)/i;
4
+ const RATE_LIMIT = /(rate limit|temporar(?:y|ily)|ban|abuse|too many)/i;
5
+ export function parsePunktumWhois(raw) {
6
+ const text = raw.trim();
7
+ if (!text) {
8
+ return unknown();
9
+ }
10
+ if (RATE_LIMIT.test(text)) {
11
+ return {
12
+ available: null,
13
+ status: "rate_limited",
14
+ confidence: "unknown"
15
+ };
16
+ }
17
+ if (NOT_FOUND.test(text)) {
18
+ return {
19
+ available: true,
20
+ status: "probably_available",
21
+ confidence: "whois_inferred"
22
+ };
23
+ }
24
+ if (WAITING_LIST.test(text)) {
25
+ return {
26
+ available: false,
27
+ status: "offered_to_waiting_list",
28
+ confidence: "public_registry_lookup"
29
+ };
30
+ }
31
+ if (DOMAIN_FIELD.test(text)) {
32
+ return {
33
+ available: false,
34
+ status: "registered",
35
+ confidence: "public_registry_lookup"
36
+ };
37
+ }
38
+ return unknown();
39
+ }
40
+ function unknown() {
41
+ return {
42
+ available: null,
43
+ status: "unknown",
44
+ confidence: "unknown"
45
+ };
46
+ }
47
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAEA,MAAM,SAAS,GAAG,6CAA6C,CAAC;AAChE,MAAM,YAAY,GAAG,eAAe,CAAC;AACrC,MAAM,YAAY,GAAG,2DAA2D,CAAC;AACjF,MAAM,UAAU,GAAG,oDAAoD,CAAC;AAExE,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAExB,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,OAAO,EAAE,CAAC;IACnB,CAAC;IAED,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,SAAS,EAAE,IAAI;YACf,MAAM,EAAE,cAAc;YACtB,UAAU,EAAE,SAAS;SACtB,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,OAAO;YACL,SAAS,EAAE,IAAI;YACf,MAAM,EAAE,oBAAoB;YAC5B,UAAU,EAAE,gBAAgB;SAC7B,CAAC;IACJ,CAAC;IAED,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,OAAO;YACL,SAAS,EAAE,KAAK;YAChB,MAAM,EAAE,yBAAyB;YACjC,UAAU,EAAE,wBAAwB;SACrC,CAAC;IACJ,CAAC;IAED,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,OAAO;YACL,SAAS,EAAE,KAAK;YAChB,MAAM,EAAE,YAAY;YACpB,UAAU,EAAE,wBAAwB;SACrC,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,EAAE,CAAC;AACnB,CAAC;AAED,SAAS,OAAO;IACd,OAAO;QACL,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,SAAS;QACjB,UAAU,EAAE,SAAS;KACtB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { AvailabilityResult, WhoisParseResult } from "./types.js";
2
+ export declare function invalidResult(input: string, checkedAt: string): AvailabilityResult;
3
+ export declare function availabilityResult(unicodeDomain: string, asciiDomain: string, parsed: WhoisParseResult, checkedAt: string, cacheHit: boolean, ttlSeconds: number | null): AvailabilityResult;
4
+ //# sourceMappingURL=result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEvE,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,kBAAkB,CAkBlF;AAED,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,OAAO,EACjB,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,kBAAkB,CAgBpB"}
package/dist/result.js ADDED
@@ -0,0 +1,36 @@
1
+ export function invalidResult(input, checkedAt) {
2
+ const domain = input.trim();
3
+ return {
4
+ domain,
5
+ unicodeDomain: domain,
6
+ asciiDomain: domain,
7
+ available: null,
8
+ status: "invalid",
9
+ confidence: "unknown",
10
+ source: "punktum_whois",
11
+ registrationRequiredForConfirmation: true,
12
+ checkedAt,
13
+ cache: {
14
+ hit: false,
15
+ ttlSeconds: null
16
+ }
17
+ };
18
+ }
19
+ export function availabilityResult(unicodeDomain, asciiDomain, parsed, checkedAt, cacheHit, ttlSeconds) {
20
+ return {
21
+ domain: unicodeDomain,
22
+ unicodeDomain,
23
+ asciiDomain,
24
+ available: parsed.available,
25
+ status: parsed.status,
26
+ confidence: parsed.confidence,
27
+ source: "punktum_whois",
28
+ registrationRequiredForConfirmation: true,
29
+ checkedAt,
30
+ cache: {
31
+ hit: cacheHit,
32
+ ttlSeconds
33
+ }
34
+ };
35
+ }
36
+ //# sourceMappingURL=result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result.js","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,SAAiB;IAC5D,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAE5B,OAAO;QACL,MAAM;QACN,aAAa,EAAE,MAAM;QACrB,WAAW,EAAE,MAAM;QACnB,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,SAAS;QACjB,UAAU,EAAE,SAAS;QACrB,MAAM,EAAE,eAAe;QACvB,mCAAmC,EAAE,IAAI;QACzC,SAAS;QACT,KAAK,EAAE;YACL,GAAG,EAAE,KAAK;YACV,UAAU,EAAE,IAAI;SACjB;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,aAAqB,EACrB,WAAmB,EACnB,MAAwB,EACxB,SAAiB,EACjB,QAAiB,EACjB,UAAyB;IAEzB,OAAO;QACL,MAAM,EAAE,aAAa;QACrB,aAAa;QACb,WAAW;QACX,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,MAAM,EAAE,eAAe;QACvB,mCAAmC,EAAE,IAAI;QACzC,SAAS;QACT,KAAK,EAAE;YACL,GAAG,EAAE,QAAQ;YACb,UAAU;SACX;KACF,CAAC;AACJ,CAAC"}
package/dist/ttl.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { AvailabilityStatus } from "./types.js";
2
+ export declare function ttlForStatus(status: AvailabilityStatus): number;
3
+ //# sourceMappingURL=ttl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ttl.d.ts","sourceRoot":"","sources":["../src/ttl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,wBAAgB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAa/D"}
package/dist/ttl.js ADDED
@@ -0,0 +1,15 @@
1
+ export function ttlForStatus(status) {
2
+ switch (status) {
3
+ case "registered":
4
+ case "offered_to_waiting_list":
5
+ return 60 * 60 * 12;
6
+ case "probably_available":
7
+ return 60 * 10;
8
+ case "unknown":
9
+ case "rate_limited":
10
+ return 60 * 2;
11
+ case "invalid":
12
+ return 0;
13
+ }
14
+ }
15
+ //# sourceMappingURL=ttl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ttl.js","sourceRoot":"","sources":["../src/ttl.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,YAAY,CAAC,MAA0B;IACrD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,YAAY,CAAC;QAClB,KAAK,yBAAyB;YAC5B,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;QACtB,KAAK,oBAAoB;YACvB,OAAO,EAAE,GAAG,EAAE,CAAC;QACjB,KAAK,SAAS,CAAC;QACf,KAAK,cAAc;YACjB,OAAO,EAAE,GAAG,CAAC,CAAC;QAChB,KAAK,SAAS;YACZ,OAAO,CAAC,CAAC;IACb,CAAC;AACH,CAAC"}
@@ -0,0 +1,62 @@
1
+ export type AvailabilityStatus = "probably_available" | "registered" | "offered_to_waiting_list" | "invalid" | "rate_limited" | "unknown";
2
+ export type AvailabilityConfidence = "whois_inferred" | "public_registry_lookup" | "unknown";
3
+ export type AvailabilityResult = {
4
+ domain: string;
5
+ unicodeDomain: string;
6
+ asciiDomain: string;
7
+ available: boolean | null;
8
+ status: AvailabilityStatus;
9
+ confidence: AvailabilityConfidence;
10
+ source: "punktum_whois";
11
+ registrationRequiredForConfirmation: true;
12
+ checkedAt: string;
13
+ cache: {
14
+ hit: boolean;
15
+ ttlSeconds: number | null;
16
+ };
17
+ };
18
+ export type NormalizedDomain = {
19
+ input: string;
20
+ unicodeDomain: string;
21
+ asciiDomain: string;
22
+ };
23
+ export type WhoisParseResult = Pick<AvailabilityResult, "available" | "status" | "confidence">;
24
+ export type AvailabilityOptions = {
25
+ bypassCache?: boolean;
26
+ timeoutMs?: number;
27
+ maxQueueWaitMs?: number;
28
+ clock?: Clock;
29
+ cache?: AvailabilityCache;
30
+ rateLimiter?: RateLimiter;
31
+ whoisClient?: WhoisClient;
32
+ };
33
+ export type WhoisClient = {
34
+ query(domain: string, timeoutMs: number): Promise<string>;
35
+ };
36
+ export type CachedAvailability = {
37
+ result: AvailabilityResult;
38
+ expiresAtMs: number;
39
+ };
40
+ export type AvailabilityCache = {
41
+ get(domain: string, nowMs: number): Promise<CachedAvailability | null>;
42
+ set(domain: string, result: AvailabilityResult, ttlSeconds: number, nowMs: number): Promise<void>;
43
+ clear(): Promise<void>;
44
+ close?(): void;
45
+ };
46
+ export type Clock = {
47
+ now(): number;
48
+ sleep(ms: number): Promise<void>;
49
+ };
50
+ export type RateLimitRequest = {
51
+ intervalMs: number;
52
+ maxWaitMs: number;
53
+ clock: Clock;
54
+ };
55
+ export type RateLimitResult = {
56
+ acquired: boolean;
57
+ waitedMs: number;
58
+ };
59
+ export type RateLimiter = {
60
+ waitForTurn(request: RateLimitRequest): Promise<RateLimitResult>;
61
+ };
62
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAC1B,oBAAoB,GACpB,YAAY,GACZ,yBAAyB,GACzB,SAAS,GACT,cAAc,GACd,SAAS,CAAC;AAEd,MAAM,MAAM,sBAAsB,GAC9B,gBAAgB,GAChB,wBAAwB,GACxB,SAAS,CAAC;AAEd,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,UAAU,EAAE,sBAAsB,CAAC;IACnC,MAAM,EAAE,eAAe,CAAC;IACxB,mCAAmC,EAAE,IAAI,CAAC;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE;QACL,GAAG,EAAE,OAAO,CAAC;QACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;KAC3B,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,IAAI,CACjC,kBAAkB,EAClB,WAAW,GAAG,QAAQ,GAAG,YAAY,CACtC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3D,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;IACvE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,KAAK,CAAC,IAAI,IAAI,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,IAAI,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CAClE,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,8 @@
1
+ import type { WhoisClient } from "./types.js";
2
+ export declare class PunktumWhoisClient implements WhoisClient {
3
+ private readonly host;
4
+ private readonly port;
5
+ constructor(host?: string, port?: number);
6
+ query(domain: string, timeoutMs: number): Promise<string>;
7
+ }
8
+ //# sourceMappingURL=whois-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whois-client.d.ts","sourceRoot":"","sources":["../src/whois-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,qBAAa,kBAAmB,YAAW,WAAW;IACxC,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAuB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAhD,IAAI,SAAqB,EAAmB,IAAI,SAAK;IAElF,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CA6B1D"}
@@ -0,0 +1,36 @@
1
+ import net from "node:net";
2
+ export class PunktumWhoisClient {
3
+ host;
4
+ port;
5
+ constructor(host = "whois.punktum.dk", port = 43) {
6
+ this.host = host;
7
+ this.port = port;
8
+ }
9
+ query(domain, timeoutMs) {
10
+ return new Promise((resolve, reject) => {
11
+ const socket = net.createConnection({ host: this.host, port: this.port });
12
+ const chunks = [];
13
+ let finished = false;
14
+ const finish = (error) => {
15
+ if (finished)
16
+ return;
17
+ finished = true;
18
+ socket.destroy();
19
+ if (error) {
20
+ reject(error);
21
+ return;
22
+ }
23
+ resolve(Buffer.concat(chunks).toString("utf8"));
24
+ };
25
+ socket.setTimeout(timeoutMs);
26
+ socket.on("connect", () => {
27
+ socket.write(`${domain}\r\n`);
28
+ });
29
+ socket.on("data", chunk => chunks.push(Buffer.from(chunk)));
30
+ socket.on("end", () => finish());
31
+ socket.on("timeout", () => finish(new Error("WHOIS query timed out.")));
32
+ socket.on("error", error => finish(error));
33
+ });
34
+ }
35
+ }
36
+ //# sourceMappingURL=whois-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whois-client.js","sourceRoot":"","sources":["../src/whois-client.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AAG3B,MAAM,OAAO,kBAAkB;IACA;IAA4C;IAAzE,YAA6B,OAAO,kBAAkB,EAAmB,OAAO,EAAE;QAArD,SAAI,GAAJ,IAAI,CAAqB;QAAmB,SAAI,GAAJ,IAAI,CAAK;IAAG,CAAC;IAEtF,KAAK,CAAC,MAAc,EAAE,SAAiB;QACrC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,MAAM,GAAG,GAAG,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YAC1E,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,IAAI,QAAQ,GAAG,KAAK,CAAC;YAErB,MAAM,MAAM,GAAG,CAAC,KAAa,EAAQ,EAAE;gBACrC,IAAI,QAAQ;oBAAE,OAAO;gBACrB,QAAQ,GAAG,IAAI,CAAC;gBAChB,MAAM,CAAC,OAAO,EAAE,CAAC;gBAEjB,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,CAAC,KAAK,CAAC,CAAC;oBACd,OAAO;gBACT,CAAC;gBAED,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;YAClD,CAAC,CAAC;YAEF,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;YAC7B,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;gBACxB,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,MAAM,CAAC,CAAC;YAChC,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC5D,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;YACjC,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC;YACxE,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "dk-domain-agent-core",
3
+ "version": "0.1.0",
4
+ "description": "Core .dk WHOIS availability inference library.",
5
+ "type": "module",
6
+ "license": "Apache-2.0",
7
+ "sideEffects": false,
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "keywords": [
12
+ "dk",
13
+ "domain",
14
+ "whois",
15
+ "availability",
16
+ "punktum"
17
+ ],
18
+ "main": "dist/index.js",
19
+ "types": "dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "default": "./dist/index.js"
24
+ }
25
+ },
26
+ "scripts": {
27
+ "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
28
+ "build": "npm run clean && tsc -p tsconfig.json",
29
+ "test": "vitest run",
30
+ "typecheck": "tsc -p tsconfig.json --noEmit"
31
+ },
32
+ "dependencies": {}
33
+ }