domain-quotes 0.0.10

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 (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +149 -0
  3. package/package.json +36 -0
  4. package/todo.txt +5 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Justice Ogbonna
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,149 @@
1
+ # DomainPrices
2
+
3
+ [![Build](https://github.com/namewiz/domainprices/actions/workflows/build.yml/badge.svg)](https://github.com/namewiz/domainprices/actions/workflows/build.yml)
4
+ [![Test](https://github.com/namewiz/domainprices/actions/workflows/test.yml/badge.svg)](https://github.com/namewiz/domainprices/actions/workflows/test.yml)
5
+ [![NPM](http://img.shields.io/npm/v/domainprices.svg)](https://www.npmjs.com/package/domainprices)
6
+ [![License](https://img.shields.io/npm/l/domainprices.svg)](https://github.com/namewiz/domainprices/blob/main/LICENSE)
7
+
8
+
9
+ DomainPrices is a lightweight TypeScript/JavaScript library to compute domain registration prices across currencies with discounts and VAT, using curated datasets.
10
+
11
+ Includes:
12
+ - Extension support based on unified registrar price list (OpenProvider/NIRA)
13
+ - Currency conversion via remote exchange rates
14
+ - VAT calculation per currency (US, GB, DE, NG mapping)
15
+ - Optional discount codes with max-or-stack policy
16
+ - Configurable markup to increase base prices before taxes/discounts
17
+ - Clean ESM API with TypeScript types
18
+
19
+ Install
20
+
21
+ ```bash
22
+ npm i domainprices
23
+ ```
24
+
25
+ Usage
26
+
27
+ ```ts
28
+ import { getDefaultPrice, DomainPrices, DEFAULTS_Sept2025 } from 'domainprices';
29
+
30
+ // Quick price (uses bundled defaults)
31
+ const quote = await getDefaultPrice('com', 'USD', { discountCodes: ['SAVE10'] });
32
+ // → { extension, currency, basePrice, discount, tax, totalPrice, symbol }
33
+
34
+ // Advanced: custom or explicit config via the class
35
+ const dp = new DomainPrices(DEFAULTS_Sept2025); // or provide your own DomainPricesConfig
36
+ const eur = await dp.getPrice('example.com', 'EUR', { discountPolicy: 'stack' });
37
+
38
+ // Add a 15% markup before discounts/taxes
39
+ const withMarkup = new DomainPrices({
40
+ ...DEFAULTS_Sept2025,
41
+ markup: { type: 'percentage', value: 0.15 },
42
+ });
43
+ const quoteWithMarkup = await withMarkup.getPrice('example.com', 'USD', { discountCodes: ['SAVE10'] });
44
+ ```
45
+
46
+ API
47
+
48
+ - `getDefaultPrice(extension: string, currency: string, options?: GetPriceOptions): Promise<PriceQuote>`
49
+ - Computes price for a TLD/SLD extension (e.g. `com`, `com.ng`, `.org`) using the bundled defaults.
50
+ - `options`
51
+ - `discountCodes?: string[]` – one or more codes; case-insensitive.
52
+ - `now?: number | Date` – inject time for deterministic tests.
53
+ - `discountPolicy?: 'stack' | 'max'` – default `'max'` (highest single discount only).
54
+
55
+ - `class DomainPrices(config: DomainPricesConfig)`
56
+ - `getPrice(extension: string, currency: string, options?: GetPriceOptions): Promise<PriceQuote>` – same behavior as above, but uses the provided config.
57
+ - `DEFAULTS_Sept2025: DomainPricesConfig` – exported snapshot config used by `getDefaultPrice`.
58
+
59
+ - `listSupportedExtensions(): string[]`
60
+ - All extensions with a non-zero price in the dataset.
61
+
62
+ - `isSupportedExtension(extOrDomain: string): boolean`
63
+ - Accepts an extension or a full domain (resolved by longest-known suffix match against bundled price data).
64
+
65
+ - `listSupportedCurrencies(): string[]`
66
+ - Currently returns `['USD','GBP','EUR','NGN']`. These map to VAT via country ISO codes.
67
+
68
+ - `isSupportedCurrency(code: string): boolean`
69
+
70
+ Types
71
+
72
+ ```ts
73
+ type DiscountPolicy = 'stack' | 'max';
74
+
75
+ interface GetPriceOptions {
76
+ discountCodes?: string[];
77
+ now?: number | Date;
78
+ discountPolicy?: DiscountPolicy;
79
+ transaction?: 'create' | 'renew' | 'restore' | 'transfer'; // default 'create'
80
+ }
81
+
82
+ type MarkupType = 'percentage' | 'fixed_usd';
83
+
84
+ interface PriceMarkup {
85
+ type: MarkupType; // percentage -> 0.2 === +20%, fixed_usd -> +$ value before conversion
86
+ value: number;
87
+ }
88
+
89
+ interface PriceQuote {
90
+ extension: string;
91
+ currency: string;
92
+ basePrice: number;
93
+ discount: number;
94
+ tax: number;
95
+ totalPrice: number;
96
+ symbol: string;
97
+ transaction: 'create' | 'renew' | 'restore' | 'transfer';
98
+ }
99
+
100
+ interface ExchangeRateData {
101
+ countryCode: string;
102
+ currencyName: string;
103
+ currencySymbol: string;
104
+ currencyCode: string;
105
+ exchangeRate: number;
106
+ inverseRate: number;
107
+ }
108
+
109
+ interface DiscountConfig {
110
+ rate: number; // e.g. 0.1 for 10%
111
+ extensions: string[]; // eligible extensions
112
+ startAt: string; // ISO timestamp
113
+ endAt: string; // ISO timestamp
114
+ }
115
+
116
+ interface DomainPricesConfig {
117
+ createPrices: Record<string, number>; // base USD prices for create
118
+ // Optional price tables per transaction type (all USD). Falls back to `createPrices` when absent.
119
+ renewPrices?: Record<string, number>;
120
+ restorePrices?: Record<string, number>;
121
+ transferPrices?: Record<string, number>;
122
+ exchangeRates: ExchangeRateData[]; // currency conversion data
123
+ vatRates: Record<string, number>; // ISO country code → VAT rate
124
+ discounts: Record<string, DiscountConfig>; // discount code → config
125
+ markup?: PriceMarkup; // optional markup applied before conversion
126
+ }
127
+ ```
128
+
129
+ Errors
130
+
131
+ - `UnsupportedExtensionError` with `code = 'ERR_UNSUPPORTED_EXTENSION'`
132
+ - `UnsupportedCurrencyError` with `code = 'ERR_UNSUPPORTED_CURRENCY'`
133
+
134
+ Notes
135
+
136
+ - Rounding is to 2 decimal places at each step to keep totals predictable (`base`, `discount`, `tax`, `total`).
137
+ - VAT mapping is intentionally narrow and explicit by currency → country: `USD → US (0)`, `GBP → GB (0.2)`, `EUR → DE (0.19)`, `NGN → NG (0.075)`.
138
+ - Price and exchange-rate data are fetched from maintained remote sources at import time:
139
+ - Prices: `https://raw.githubusercontent.com/namewiz/registrar-pricelist/refs/heads/main/data/unified-create-prices.csv`
140
+ - Exchange rates: `https://raw.githubusercontent.com/namewiz/registrar-pricelist/refs/heads/main/data/exchange-rates.json`
141
+ These are cached in-memory for the life of the process.
142
+
143
+ ## Testing
144
+
145
+ ```bash
146
+ npm test
147
+ ```
148
+
149
+ The test suite uses Node’s built-in `node:test` runner and builds the library first.
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "domain-quotes",
3
+ "version": "0.0.10",
4
+ "description": "Fast multi-currency domain price checker library across registrars.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "scripts": {
8
+ "build": "npx -y esbuild src/index.ts --bundle --minify --format=esm --target=esnext --outdir=dist/ --platform=neutral",
9
+ "postbuild": "cp dist/index.js docs/domainprices.js",
10
+ "test": "npm run build && node --test",
11
+ "start": "npm run build && cp dist/index.js docs/domainprices.js && npx serve docs"
12
+ },
13
+ "homepage": "https://namewiz.github.io/domainprices",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/namewiz/domainprices.git"
17
+ },
18
+ "bugs": {
19
+ "url": "https://github.com/namewiz/domainprices/issues"
20
+ },
21
+ "author": "Justice Ogbonna <dev@justiceo.com>",
22
+ "keywords": [
23
+ "domain-name",
24
+ "domain-price",
25
+ "domain-availability",
26
+ "price-check"
27
+ ],
28
+ "exports": {
29
+ ".": "./dist/index.js"
30
+ },
31
+ "license": "MIT",
32
+ "engines": {
33
+ "node": "^22.12.0",
34
+ "npm": "^10.9.0"
35
+ }
36
+ }
package/todo.txt ADDED
@@ -0,0 +1,5 @@
1
+
2
+ "comment": "organizational email constraint, origin website, max # of years, whether it can be combined with other coupons, user country code, new customer only",
3
+ "comment2": "doesn't support fixed amount intentionally, avoid -ve charges, ",
4
+ "comment3": "each code can be used an unlimited number of times between start and end",
5
+ "comment4": "Alt: enable use:once or use:once_per_user",