chrono-age 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/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # age-calc
2
+
3
+ **"Bulletproof" Age Calculation for High-Scale Backend & Frontend**
4
+
5
+ `age-calc` is a zero-dependency, environment-agnostic TypeScript library designed for precision age calculation. It is optimized for correctness, memory safety, and performance.
6
+
7
+ ## Features
8
+
9
+ - **Environment Agnostic**: Works perfectly in Node.js, Bun, Deno, and Browsers.
10
+ - **Zero Dependencies**: Lightweight and secure.
11
+ - **Type-Safe**: Written in TypeScript with strict types and `Readonly` interfaces.
12
+ - **Defensive**: Validates inputs rigorously to prevent runtime crashes (no `undefined` errors).
13
+ - **Correctness**: Handles leap years and edge cases accurately.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install age-calc
19
+ # or
20
+ bun add age-calc
21
+ # or
22
+ yarn add age-calc
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Basic Usage
28
+
29
+ ```typescript
30
+ import { calculateAge, calculateAgeDetail } from 'age-calc';
31
+
32
+ const birthDate = new Date('1990-01-01');
33
+ const age = calculateAge({ birthDate });
34
+
35
+ console.log(age); // e.g., 33 (depending on today's date)
36
+
37
+ // Get detailed breakdown
38
+ const detail = calculateAgeDetail({
39
+ birthDate,
40
+ targetDate: new Date('2023-05-20')
41
+ });
42
+ console.log(detail); // { years: 33, months: 4, days: 19 }
43
+ ```
44
+
45
+ ### Backend Integration (Express / ElysiaJS)
46
+
47
+ `age-calc` throws typed errors (`InvalidDateError`, `FutureDateError`) allowing you to return proper HTTP 400 responses.
48
+
49
+ **Express & TypeScript Example:**
50
+
51
+ ```typescript
52
+ import express from 'express';
53
+ import { calculateAge, InvalidDateError, FutureDateError } from 'age-calc';
54
+
55
+ const app = express();
56
+ app.use(express.json());
57
+
58
+ app.post('/check-age', (req, res) => {
59
+ try {
60
+ const { birthDateStr } = req.body;
61
+ const birthDate = new Date(birthDateStr);
62
+
63
+ // Calculate age (defaults targetDate to now)
64
+ const age = calculateAge({ birthDate });
65
+
66
+ res.json({ age });
67
+ } catch (error) {
68
+ if (error instanceof InvalidDateError || error instanceof FutureDateError) {
69
+ // 400 Bad Request
70
+ res.status(400).json({ error: error.message });
71
+ } else {
72
+ // 500 Internal Server Error
73
+ res.status(500).json({ error: 'Internal Server Error' });
74
+ }
75
+ }
76
+ });
77
+ ```
78
+
79
+ ## API Reference
80
+
81
+ ### `calculateAge(input: AgeInput): number`
82
+ Calculates the age in full years.
83
+
84
+ ### `calculateAgeDetail(input: AgeInput): AgeDetail`
85
+ Calculates age in years, months, and days.
86
+
87
+ ### `validateInput(input: AgeInput): void`
88
+ Runs validation logic explicitly. Throws errors if invalid.
89
+
90
+ ### Types
91
+ - `AgeInput`: `{ readonly birthDate: Date; readonly targetDate?: Date; }`
92
+ - `AgeDetail`: `{ years: number; months: number; days: number; }`
93
+
94
+ ## Benchmarks
95
+ Designed for low memory footprint. Allocates minimal objects.
96
+ Zero external dependencies ensures fast install and startup.
97
+
98
+ ## License
99
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ var{defineProperty:L,getOwnPropertyNames:S,getOwnPropertyDescriptor:U}=Object,V=Object.prototype.hasOwnProperty;var O=new WeakMap,W=(x)=>{var A=O.get(x),j;if(A)return A;if(A=L({},"__esModule",{value:!0}),x&&typeof x==="object"||typeof x==="function")S(x).map((q)=>!V.call(A,q)&&L(A,q,{get:()=>x[q],enumerable:!(j=U(x,q))||j.enumerable}));return O.set(x,A),A};var X=(x,A)=>{for(var j in A)L(x,j,{get:A[j],enumerable:!0,configurable:!0,set:(q)=>A[j]=()=>q})};var Y={};X(Y,{validateInput:()=>J,calculateAgeDetail:()=>Q,calculateAge:()=>P,InvalidDateError:()=>B,FutureDateError:()=>H,AgeCalcError:()=>F});module.exports=W(Y);class F extends Error{constructor(x){super(x);this.name="AgeCalcError"}}class B extends F{constructor(x="Invalid date provided"){super(x);this.name="InvalidDateError"}}class H extends F{constructor(x="Birth date cannot be in the future"){super(x);this.name="FutureDateError"}}function J(x){if(!x)throw new B("Input object is required");let{birthDate:A,targetDate:j=new Date}=x;if(!(A instanceof Date)||isNaN(A.getTime()))throw new B("Invalid birth date");if(!(j instanceof Date)||isNaN(j.getTime()))throw new B("Invalid target date");if(A.getTime()>j.getTime())throw new H(`Birth date ${A.toISOString()} is after target date ${j.toISOString()}`)}function P(x){J(x);let{birthDate:A,targetDate:j=new Date}=x,q=j.getFullYear()-A.getFullYear(),z=j.getMonth()-A.getMonth();if(z<0||z===0&&j.getDate()<A.getDate())q--;return q}function Q(x){J(x);let{birthDate:A,targetDate:j=new Date}=x,q=j.getFullYear()-A.getFullYear(),z=j.getMonth()-A.getMonth(),K=j.getDate()-A.getDate();if(K<0){z--;let R=new Date(j.getFullYear(),j.getMonth(),0);K+=R.getDate()}if(z<0)q--,z+=12;return{years:q,months:z,days:K}}
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ class F extends Error{constructor(x){super(x);this.name="AgeCalcError"}}class z extends F{constructor(x="Invalid date provided"){super(x);this.name="InvalidDateError"}}class H extends F{constructor(x="Birth date cannot be in the future"){super(x);this.name="FutureDateError"}}function J(x){if(!x)throw new z("Input object is required");let{birthDate:j,targetDate:A=new Date}=x;if(!(j instanceof Date)||isNaN(j.getTime()))throw new z("Invalid birth date");if(!(A instanceof Date)||isNaN(A.getTime()))throw new z("Invalid target date");if(j.getTime()>A.getTime())throw new H(`Birth date ${j.toISOString()} is after target date ${A.toISOString()}`)}function O(x){J(x);let{birthDate:j,targetDate:A=new Date}=x,B=A.getFullYear()-j.getFullYear(),q=A.getMonth()-j.getMonth();if(q<0||q===0&&A.getDate()<j.getDate())B--;return B}function P(x){J(x);let{birthDate:j,targetDate:A=new Date}=x,B=A.getFullYear()-j.getFullYear(),q=A.getMonth()-j.getMonth(),K=A.getDate()-j.getDate();if(K<0){q--;let L=new Date(A.getFullYear(),A.getMonth(),0);K+=L.getDate()}if(q<0)B--,q+=12;return{years:B,months:q,days:K}}export{J as validateInput,P as calculateAgeDetail,O as calculateAge,z as InvalidDateError,H as FutureDateError,F as AgeCalcError};
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "chrono-age",
3
+ "version": "1.0.0",
4
+ "description": "A bulletproof, environment-agnostic age calculator for high-scale backends and frontends.",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "build": "bun build ./src/index.ts --outfile ./dist/index.js --target node --format cjs --minify && bun build ./src/index.ts --outfile ./dist/index.mjs --target node --format esm --minify",
17
+ "test": "bun test",
18
+ "lint": "bun x eslint src/**/*.ts",
19
+ "prepublishOnly": "bun run build"
20
+ },
21
+ "keywords": [
22
+ "age",
23
+ "calculator",
24
+ "date",
25
+ "validator",
26
+ "typescript",
27
+ "zero-dependency"
28
+ ],
29
+ "author": "",
30
+ "license": "MIT",
31
+ "devDependencies": {
32
+ "bun-types": "latest",
33
+ "typescript": "^5.0.0"
34
+ },
35
+ "peerDependencies": {
36
+ "typescript": "^5.0.0"
37
+ }
38
+ }
package/verify.ts ADDED
@@ -0,0 +1,41 @@
1
+ import { calculateAge, calculateAgeDetail, validateInput, InvalidDateError } from "./dist/index";
2
+
3
+ console.log("Starting verification...");
4
+
5
+ try {
6
+ // Test 1: Basic Age
7
+ const age = calculateAge({ birthDate: new Date("1990-01-01"), targetDate: new Date("2023-01-01") });
8
+ if (age !== 33) throw new Error(`Test 1 Failed: Expected 33, got ${age}`);
9
+ console.log("Test 1 Passed: Basic Age");
10
+
11
+ // Test 2: Leap Year
12
+ const ageLeap = calculateAge({ birthDate: new Date("2000-02-29"), targetDate: new Date("2001-02-28") });
13
+ if (ageLeap !== 0) throw new Error(`Test 2 Failed: Expected 0, got ${ageLeap}`);
14
+ console.log("Test 2 Passed: Leap Year boundary");
15
+
16
+ // Test 3: Validation
17
+ try {
18
+ // @ts-ignore
19
+ validateInput(null);
20
+ throw new Error("Test 3 Failed: Should have thrown validation error");
21
+ } catch (e) {
22
+ if (e instanceof InvalidDateError) {
23
+ console.log("Test 3 Passed: Validation caught null");
24
+ } else {
25
+ throw e;
26
+ }
27
+ }
28
+
29
+ // Test 4: Details
30
+ const details = calculateAgeDetail({ birthDate: new Date("1990-01-01"), targetDate: new Date("1991-02-05") });
31
+ if (details.years !== 1 || details.months !== 1 || details.days !== 4) {
32
+ throw new Error(`Test 4 Failed: Details mismatch ${JSON.stringify(details)}`);
33
+ }
34
+ console.log("Test 4 Passed: details");
35
+
36
+ console.log("ALL TESTS PASSED MANUALLY");
37
+
38
+ } catch (e) {
39
+ console.error(e);
40
+ process.exit(1);
41
+ }