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.
- package/LICENSE +21 -0
- package/README.md +149 -0
- package/package.json +36 -0
- 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
|
+
[](https://github.com/namewiz/domainprices/actions/workflows/build.yml)
|
|
4
|
+
[](https://github.com/namewiz/domainprices/actions/workflows/test.yml)
|
|
5
|
+
[](https://www.npmjs.com/package/domainprices)
|
|
6
|
+
[](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",
|