@rdapify/pro 0.2.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/LICENSE ADDED
@@ -0,0 +1,35 @@
1
+ RDAPify Pro — Commercial License Agreement
2
+
3
+ Copyright (c) 2025 RDAPify Authors. All rights reserved.
4
+
5
+ This software is licensed, not sold. By using this software, you agree
6
+ to the following terms:
7
+
8
+ 1. GRANT OF LICENSE
9
+ A valid license key is required to use this software. Each license key
10
+ is granted to a single organization and covers the number of developers
11
+ specified in the purchased plan.
12
+
13
+ 2. PERMITTED USE
14
+ - Use in commercial and internal applications
15
+ - Use by the number of developers covered by your license
16
+ - Deployment to unlimited production environments
17
+
18
+ 3. RESTRICTIONS
19
+ - You may NOT redistribute, sublicense, or share the source code
20
+ - You may NOT reverse engineer, decompile, or disassemble the software
21
+ - You may NOT share, transfer, or publish your license key
22
+ - You may NOT remove or alter any proprietary notices
23
+
24
+ 4. SUBSCRIPTION
25
+ This license is valid only during the active subscription period.
26
+ Upon expiration or cancellation, you must cease using the software.
27
+
28
+ 5. WARRANTY DISCLAIMER
29
+ THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
30
+
31
+ 6. LIMITATION OF LIABILITY
32
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DAMAGES ARISING
33
+ FROM THE USE OF THIS SOFTWARE.
34
+
35
+ For licensing inquiries: licensing@rdapify.dev
package/README.md ADDED
@@ -0,0 +1,176 @@
1
+ # @rdapify/pro
2
+
3
+ Commercial extension for [rdapify](https://github.com/rdapify/RDAPify) that adds continuous monitoring, change detection, historical tracking, analytics, and integrations on top of the open-source RDAP client.
4
+
5
+ `@rdapify/pro` is a **plugin** — it attaches to an existing `RDAPClient` instance via the `.use()` interface and does not modify or replace any core behaviour. All open-source features (SSRF protection, caching, PII redaction, middleware) remain fully operational.
6
+
7
+ ## Requirements
8
+
9
+ | Dependency | Version |
10
+ |------------|---------|
11
+ | `rdapify` (peer) | `>= 0.2.0` |
12
+ | Node.js | `>= 18` |
13
+ | License key | Required — [rdapify.dev/pricing](https://rdapify.dev/pricing) |
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install rdapify @rdapify/pro
19
+ ```
20
+
21
+ ## Quick start
22
+
23
+ ```typescript
24
+ import { RDAPClient } from 'rdapify';
25
+ import { ProPlugin } from '@rdapify/pro';
26
+
27
+ const client = new RDAPClient();
28
+ client.use(ProPlugin({ licenseKey: process.env.RDAPIFY_LICENSE_KEY! }));
29
+ ```
30
+
31
+ Once activated, six new capabilities are available on the client instance: `monitor`, `detect`, `report`, `history`, `webhooks`, and `export`.
32
+
33
+ ---
34
+
35
+ ## Modules
36
+
37
+ ### Bulk monitoring
38
+
39
+ Monitor thousands of domains or IP addresses on a recurring schedule. The monitor runs queries at a configurable interval, compares successive snapshots, and fires callbacks when changes are detected.
40
+
41
+ ```typescript
42
+ client.monitor.start(['example.com', 'example.org'], {
43
+ interval: '6h', // check every 6 hours
44
+ concurrency: 10, // max parallel RDAP queries
45
+ onChange(event) {
46
+ // event.domain — the target that changed
47
+ // event.type — 'created' | 'updated' | 'expired' | 'transferred' | 'deleted'
48
+ // event.changes — list of changed field paths
49
+ // event.previous — previous RDAP snapshot
50
+ // event.current — current RDAP snapshot
51
+ console.log(`${event.domain}: ${event.type}`, event.changes);
52
+ },
53
+ });
54
+
55
+ // Stop monitoring
56
+ client.monitor.stop();
57
+ ```
58
+
59
+ ### Change detection
60
+
61
+ Compare two RDAP response snapshots and extract a structured diff. Useful for on-demand comparisons without continuous monitoring.
62
+
63
+ Detected change types:
64
+
65
+ | Type | Description |
66
+ |------|-------------|
67
+ | `nameserver_changed` | Nameserver list differs |
68
+ | `status_changed` | Domain/IP status flags changed |
69
+ | `registrar_changed` | Sponsoring registrar changed |
70
+ | `expiry_changed` | Expiration date moved |
71
+ | `contact_changed` | Registrant/admin/tech contact changed |
72
+ | `dnssec_changed` | DNSSEC delegation signer data changed |
73
+
74
+ ```typescript
75
+ const previous = await client.domain('example.com');
76
+ // ... time passes ...
77
+ const current = await client.domain('example.com');
78
+
79
+ const changes = client.detect.diff(previous, current);
80
+ // → [{ changeType: 'expiry_changed', field: 'events.expiration',
81
+ // previousValue: '2025-03-01', currentValue: '2026-03-01' }]
82
+ ```
83
+
84
+ ### Historical tracking
85
+
86
+ Record RDAP snapshots over time and query the history for a specific target.
87
+
88
+ ```typescript
89
+ // Record a snapshot (typically called from monitor.onChange)
90
+ client.history.record('example.com', 'domain', currentResponse);
91
+
92
+ // Query stored history
93
+ const entries = client.history.query({
94
+ target: 'example.com',
95
+ from: new Date('2026-01-01'),
96
+ to: new Date('2026-03-21'),
97
+ limit: 50,
98
+ });
99
+ ```
100
+
101
+ ### Analytics and reporting
102
+
103
+ Generate summary, detailed, or trend-based reports from collected query data.
104
+
105
+ ```typescript
106
+ const report = client.report.generate({
107
+ format: 'trends', // 'summary' | 'detailed' | 'trends'
108
+ from: new Date('2026-01-01'),
109
+ to: new Date('2026-03-21'),
110
+ });
111
+
112
+ console.log(report.totalQueries, report.data);
113
+ ```
114
+
115
+ ### Webhook integrations
116
+
117
+ Push change notifications to Slack, Discord, Microsoft Teams, or any HTTP endpoint.
118
+
119
+ ```typescript
120
+ client.webhooks.register({
121
+ provider: 'slack', // 'slack' | 'discord' | 'teams' | 'custom'
122
+ url: process.env.SLACK_WEBHOOK_URL!,
123
+ events: ['status_changed', 'expiry_changed'],
124
+ });
125
+
126
+ // Notifications are sent automatically when monitor detects changes.
127
+ // Manual dispatch:
128
+ await client.webhooks.notify({ domain: 'example.com', type: 'expiry_changed' });
129
+ ```
130
+
131
+ ### Data export
132
+
133
+ Export query results or historical snapshots to JSON or CSV.
134
+
135
+ ```typescript
136
+ const csv = client.export.export(entries, {
137
+ format: 'csv',
138
+ fields: ['target', 'capturedAt', 'snapshot.registrar.name'],
139
+ });
140
+ ```
141
+
142
+ ---
143
+
144
+ ## License key format
145
+
146
+ Keys follow the pattern `RDAP-{plan}-{payload}-{signature}` and support offline validation (the payload is a signed, base64-encoded JSON). Online validation is available as an opt-in:
147
+
148
+ ```typescript
149
+ client.use(ProPlugin({
150
+ licenseKey: 'RDAP-PRO-...',
151
+ onlineValidation: true, // default: false
152
+ }));
153
+ ```
154
+
155
+ Plans: **pro** and **enterprise**. Enterprise keys unlock higher concurrency limits and priority support.
156
+
157
+ ## Architecture
158
+
159
+ ```
160
+ @rdapify/pro
161
+ ├── plugin.ts ProPlugin factory — validates license, attaches modules
162
+ ├── license/ Offline + online license validation
163
+ ├── monitoring/ BulkMonitor, ChangeDetector
164
+ ├── analytics/ Reporter (summary / detailed / trends)
165
+ ├── history/ HistoryTracker (in-memory snapshot storage)
166
+ └── integrations/ WebhookManager, Exporter (json / csv)
167
+ ```
168
+
169
+ Only `plugin.ts` touches `rdapify` internals. All other modules are self-contained and communicate through the plugin's `install()` hook.
170
+
171
+ ## License
172
+
173
+ Proprietary. A commercial license is required for production use.
174
+
175
+ See [LICENSE](LICENSE) for full terms.
176
+ Purchase at [rdapify.dev/pricing](https://rdapify.dev/pricing).
package/index.d.ts ADDED
@@ -0,0 +1,245 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /* auto-generated by NAPI-RS */
5
+
6
+ export interface ReportOptions {
7
+ /** "summary" | "detailed" | "trends". Default: "summary". */
8
+ format?: string
9
+ /** ISO-8601 — include only entries captured on or after this timestamp. */
10
+ from?: string
11
+ /** ISO-8601 — include only entries captured on or before this timestamp. */
12
+ to?: string
13
+ }
14
+ export interface TopEntry {
15
+ target: string
16
+ count: number
17
+ }
18
+ export interface ReportOutput {
19
+ generatedAt: string
20
+ format: string
21
+ totalQueries: number
22
+ registrars: Record<string, number>
23
+ statusCounts: Record<string, number>
24
+ queryTypes: Record<string, number>
25
+ topDomains: Array<TopEntry>
26
+ errorRate: number
27
+ }
28
+ export interface HistoryEntry {
29
+ target: string
30
+ queryType: string
31
+ /** JSON-serialised RDAP snapshot string. */
32
+ snapshot: string
33
+ /** ISO-8601 timestamp string. */
34
+ capturedAt: string
35
+ }
36
+ export interface HistoryQuery {
37
+ target: string
38
+ /** Filter: only entries captured on or after this ISO-8601 timestamp. */
39
+ from?: string
40
+ /** Filter: only entries captured on or before this ISO-8601 timestamp. */
41
+ to?: string
42
+ /** Return at most this many entries (most recent first). */
43
+ limit?: number
44
+ }
45
+ export interface WebhookConfig {
46
+ /** "slack" | "discord" | "teams" | "custom" */
47
+ provider: string
48
+ url: string
49
+ /** If set, only fire for these event types. */
50
+ events?: Array<string>
51
+ /** Custom HTTP headers added to the webhook request. */
52
+ headers?: Record<string, string>
53
+ }
54
+ export interface LicenseInfo {
55
+ valid: boolean
56
+ /** Present when `valid` is false. */
57
+ reason?: string
58
+ /** "pro" | "enterprise" | "trial" */
59
+ plan?: string
60
+ organization?: string
61
+ /** Unix timestamp in milliseconds (pass to `new Date(n)` in JS). */
62
+ expiresAt?: number
63
+ maxDevelopers?: number
64
+ }
65
+ export interface ChangeEvent {
66
+ target: string
67
+ /**
68
+ * "nameserver_changed" | "status_changed" | "registrar_changed" |
69
+ * "expiry_changed" | "dnssec_changed"
70
+ */
71
+ changeType: string
72
+ field: string
73
+ /** JSON-serialised previous value. */
74
+ previousValue: string
75
+ /** JSON-serialised current value. */
76
+ currentValue: string
77
+ /** ISO-8601 timestamp string. */
78
+ detectedAt: string
79
+ }
80
+ export interface MonitorEvent {
81
+ domain: string
82
+ /** "created" | "updated" | "expired" | "transferred" | "deleted" */
83
+ eventType: string
84
+ timestamp: string
85
+ changes: Array<string>
86
+ }
87
+ /** Options passed to `proPlugin()`. */
88
+ export interface ProPluginOptions {
89
+ /** License key — required. Get one at https://rdapify.dev/pricing */
90
+ licenseKey: string
91
+ /** Enable online license validation (default: false). */
92
+ onlineValidation?: boolean
93
+ }
94
+ /**
95
+ * Validated plugin descriptor returned by `proPlugin()`.
96
+ * The JS wrapper calls `install(client)` on this object.
97
+ */
98
+ export interface ProPluginDescriptor {
99
+ name: string
100
+ version: string
101
+ license: LicenseInfo
102
+ }
103
+ /**
104
+ * Validate the license key and return a plugin descriptor.
105
+ *
106
+ * Throws if the key is missing or invalid.
107
+ * The JS wrapper (`index.js`) attaches Pro modules to the client.
108
+ */
109
+ export declare function proPlugin(options: ProPluginOptions): Promise<ProPluginDescriptor>
110
+ /** Generates analytics reports from RDAP query data. */
111
+ export declare class Reporter {
112
+ constructor()
113
+ /**
114
+ * Generate a report from a slice of RDAP response objects.
115
+ *
116
+ * `data` — each element must be a JSON string
117
+ * (e.g. `history.query(...).map(e => e.snapshot)`).
118
+ */
119
+ generate(data: Array<string>, options?: ReportOptions | undefined | null): ReportOutput
120
+ }
121
+ /**
122
+ * Tracks historical RDAP data for domains/IPs over time.
123
+ * Thread-safe: the inner `Vec` is guarded by a `Mutex` so that the JS
124
+ * `afterQuery` hook and explicit `record()` calls don't race.
125
+ */
126
+ export declare class HistoryTracker {
127
+ constructor()
128
+ /**
129
+ * Record a new RDAP snapshot for `target`.
130
+ * `data` must be a JSON string (e.g. `JSON.stringify(rdapResponse)`).
131
+ */
132
+ record(target: string, queryType: string, data: string): void
133
+ /** Query history for a specific target, with optional date range and limit. */
134
+ query(options: HistoryQuery): Array<HistoryEntry>
135
+ /** Return the most recent snapshot for `target`, or `null` if none. */
136
+ latest(target: string): HistoryEntry | null
137
+ /** Total number of stored entries. */
138
+ count(): number
139
+ /** Remove all stored entries. */
140
+ clear(): void
141
+ }
142
+ /** Exports RDAP query results in JSON or CSV format. */
143
+ export declare class Exporter {
144
+ constructor()
145
+ /**
146
+ * Export `data` as a pretty-printed JSON string.
147
+ *
148
+ * `data` — each element must be a JSON string
149
+ * (e.g. `history.query(...).map(e => e.snapshot)`).
150
+ */
151
+ exportJson(data: Array<string>): string
152
+ /**
153
+ * Export `data` as CSV.
154
+ *
155
+ * `data` — each element must be a JSON string.
156
+ * `fields` — column names to include. If omitted, all top-level string/number
157
+ * keys from the first record are used.
158
+ */
159
+ exportCsv(data: Array<string>, fields?: Array<string> | undefined | null): string
160
+ }
161
+ /**
162
+ * Manages webhook integrations for change-event notifications.
163
+ * HTTP is handled by a `reqwest` async client; failures are silently ignored
164
+ * so one bad webhook cannot block the others.
165
+ */
166
+ export declare class WebhookManager {
167
+ constructor()
168
+ register(config: WebhookConfig): void
169
+ remove(url: string): void
170
+ getWebhooks(): Array<WebhookConfig>
171
+ /**
172
+ * Send `event` to all registered webhooks concurrently.
173
+ * `event` must be a JSON string (e.g. `JSON.stringify(changeEvent)`).
174
+ * Returns the number of webhooks notified (regardless of HTTP status).
175
+ */
176
+ notify(event: string): Promise<number>
177
+ }
178
+ /**
179
+ * License key format: `RDAP-{PLAN}-{base64url-payload}-{signature}`
180
+ *
181
+ * The payload is a base64url-encoded JSON object:
182
+ * `{ org: string, exp: number (ms), maxDev?: number }`
183
+ *
184
+ * The signature segment is reserved for server-side cryptographic verification.
185
+ * Use `validate_online()` for full verification and revocation checks.
186
+ */
187
+ export declare class LicenseValidator {
188
+ constructor()
189
+ /**
190
+ * Validate a license key offline.
191
+ * Decodes the embedded payload and checks expiry and plan.
192
+ * Does **not** verify the cryptographic signature.
193
+ */
194
+ static validate(key: string): LicenseInfo
195
+ /**
196
+ * Validate a license key online via the rdapify.dev license API.
197
+ * Falls back to offline validation if the API is unreachable.
198
+ */
199
+ static validateOnline(key: string): Promise<LicenseInfo>
200
+ }
201
+ /**
202
+ * Bulk domain/IP monitor.
203
+ *
204
+ * The Rust side manages targets, snapshots, and change detection.
205
+ * The JS wrapper (`index.js`) drives scheduling via `setInterval` and
206
+ * supplies RDAP query results by calling `process_results()`.
207
+ */
208
+ export declare class BulkMonitor {
209
+ /**
210
+ * Create a new BulkMonitor.
211
+ *
212
+ * `interval` — polling interval string, e.g. `"30m"`, `"1h"`, `"6h"`, `"24h"`.
213
+ * `concurrency` — max parallel queries per poll cycle (default 10).
214
+ */
215
+ constructor(targets: Array<string>, interval?: string | undefined | null, concurrency?: number | undefined | null)
216
+ /**
217
+ * Process a batch of RDAP results received from the JS side.
218
+ *
219
+ * `results` — map of `{ target → JSON string of rdapResponse }`.
220
+ * Runs change detection against stored snapshots, updates them, and
221
+ * returns monitor events.
222
+ *
223
+ * Called by the JS wrapper on each poll cycle.
224
+ */
225
+ processResults(results: Record<string, string>): Array<MonitorEvent>
226
+ getTargets(): Array<string>
227
+ getIntervalMs(): number
228
+ getConcurrency(): number
229
+ addTargets(targets: Array<string>): void
230
+ removeTargets(targets: Array<string>): void
231
+ clearSnapshots(): void
232
+ }
233
+ /** Detects specific changes in RDAP records by comparing snapshots. */
234
+ export declare class ChangeDetector {
235
+ constructor(targets: Array<string>)
236
+ /**
237
+ * Compare two RDAP response snapshots and return detected changes.
238
+ *
239
+ * Both `previous` and `current` must be JSON strings
240
+ * (e.g. `JSON.stringify(rdapResponse)`).
241
+ * Checks: nameservers, status flags, registrar name, expiry date, DNSSEC.
242
+ */
243
+ diff(previous: string, current: string): Array<ChangeEvent>
244
+ getTargets(): Array<string>
245
+ }
package/index.js ADDED
@@ -0,0 +1,322 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ /* prettier-ignore */
4
+
5
+ /* auto-generated by NAPI-RS */
6
+
7
+ const { existsSync, readFileSync } = require('fs')
8
+ const { join } = require('path')
9
+
10
+ const { platform, arch } = process
11
+
12
+ let nativeBinding = null
13
+ let localFileExisted = false
14
+ let loadError = null
15
+
16
+ function isMusl() {
17
+ // For Node 10
18
+ if (!process.report || typeof process.report.getReport !== 'function') {
19
+ try {
20
+ const lddPath = require('child_process').execSync('which ldd').toString().trim()
21
+ return readFileSync(lddPath, 'utf8').includes('musl')
22
+ } catch (e) {
23
+ return true
24
+ }
25
+ } else {
26
+ const { glibcVersionRuntime } = process.report.getReport().header
27
+ return !glibcVersionRuntime
28
+ }
29
+ }
30
+
31
+ switch (platform) {
32
+ case 'android':
33
+ switch (arch) {
34
+ case 'arm64':
35
+ localFileExisted = existsSync(join(__dirname, 'rdapify-pro.android-arm64.node'))
36
+ try {
37
+ if (localFileExisted) {
38
+ nativeBinding = require('./rdapify-pro.android-arm64.node')
39
+ } else {
40
+ nativeBinding = require('@rdapify/pro-android-arm64')
41
+ }
42
+ } catch (e) {
43
+ loadError = e
44
+ }
45
+ break
46
+ case 'arm':
47
+ localFileExisted = existsSync(join(__dirname, 'rdapify-pro.android-arm-eabi.node'))
48
+ try {
49
+ if (localFileExisted) {
50
+ nativeBinding = require('./rdapify-pro.android-arm-eabi.node')
51
+ } else {
52
+ nativeBinding = require('@rdapify/pro-android-arm-eabi')
53
+ }
54
+ } catch (e) {
55
+ loadError = e
56
+ }
57
+ break
58
+ default:
59
+ throw new Error(`Unsupported architecture on Android ${arch}`)
60
+ }
61
+ break
62
+ case 'win32':
63
+ switch (arch) {
64
+ case 'x64':
65
+ localFileExisted = existsSync(
66
+ join(__dirname, 'rdapify-pro.win32-x64-msvc.node')
67
+ )
68
+ try {
69
+ if (localFileExisted) {
70
+ nativeBinding = require('./rdapify-pro.win32-x64-msvc.node')
71
+ } else {
72
+ nativeBinding = require('@rdapify/pro-win32-x64-msvc')
73
+ }
74
+ } catch (e) {
75
+ loadError = e
76
+ }
77
+ break
78
+ case 'ia32':
79
+ localFileExisted = existsSync(
80
+ join(__dirname, 'rdapify-pro.win32-ia32-msvc.node')
81
+ )
82
+ try {
83
+ if (localFileExisted) {
84
+ nativeBinding = require('./rdapify-pro.win32-ia32-msvc.node')
85
+ } else {
86
+ nativeBinding = require('@rdapify/pro-win32-ia32-msvc')
87
+ }
88
+ } catch (e) {
89
+ loadError = e
90
+ }
91
+ break
92
+ case 'arm64':
93
+ localFileExisted = existsSync(
94
+ join(__dirname, 'rdapify-pro.win32-arm64-msvc.node')
95
+ )
96
+ try {
97
+ if (localFileExisted) {
98
+ nativeBinding = require('./rdapify-pro.win32-arm64-msvc.node')
99
+ } else {
100
+ nativeBinding = require('@rdapify/pro-win32-arm64-msvc')
101
+ }
102
+ } catch (e) {
103
+ loadError = e
104
+ }
105
+ break
106
+ default:
107
+ throw new Error(`Unsupported architecture on Windows: ${arch}`)
108
+ }
109
+ break
110
+ case 'darwin':
111
+ localFileExisted = existsSync(join(__dirname, 'rdapify-pro.darwin-universal.node'))
112
+ try {
113
+ if (localFileExisted) {
114
+ nativeBinding = require('./rdapify-pro.darwin-universal.node')
115
+ } else {
116
+ nativeBinding = require('@rdapify/pro-darwin-universal')
117
+ }
118
+ break
119
+ } catch {}
120
+ switch (arch) {
121
+ case 'x64':
122
+ localFileExisted = existsSync(join(__dirname, 'rdapify-pro.darwin-x64.node'))
123
+ try {
124
+ if (localFileExisted) {
125
+ nativeBinding = require('./rdapify-pro.darwin-x64.node')
126
+ } else {
127
+ nativeBinding = require('@rdapify/pro-darwin-x64')
128
+ }
129
+ } catch (e) {
130
+ loadError = e
131
+ }
132
+ break
133
+ case 'arm64':
134
+ localFileExisted = existsSync(
135
+ join(__dirname, 'rdapify-pro.darwin-arm64.node')
136
+ )
137
+ try {
138
+ if (localFileExisted) {
139
+ nativeBinding = require('./rdapify-pro.darwin-arm64.node')
140
+ } else {
141
+ nativeBinding = require('@rdapify/pro-darwin-arm64')
142
+ }
143
+ } catch (e) {
144
+ loadError = e
145
+ }
146
+ break
147
+ default:
148
+ throw new Error(`Unsupported architecture on macOS: ${arch}`)
149
+ }
150
+ break
151
+ case 'freebsd':
152
+ if (arch !== 'x64') {
153
+ throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
154
+ }
155
+ localFileExisted = existsSync(join(__dirname, 'rdapify-pro.freebsd-x64.node'))
156
+ try {
157
+ if (localFileExisted) {
158
+ nativeBinding = require('./rdapify-pro.freebsd-x64.node')
159
+ } else {
160
+ nativeBinding = require('@rdapify/pro-freebsd-x64')
161
+ }
162
+ } catch (e) {
163
+ loadError = e
164
+ }
165
+ break
166
+ case 'linux':
167
+ switch (arch) {
168
+ case 'x64':
169
+ if (isMusl()) {
170
+ localFileExisted = existsSync(
171
+ join(__dirname, 'rdapify-pro.linux-x64-musl.node')
172
+ )
173
+ try {
174
+ if (localFileExisted) {
175
+ nativeBinding = require('./rdapify-pro.linux-x64-musl.node')
176
+ } else {
177
+ nativeBinding = require('@rdapify/pro-linux-x64-musl')
178
+ }
179
+ } catch (e) {
180
+ loadError = e
181
+ }
182
+ } else {
183
+ localFileExisted = existsSync(
184
+ join(__dirname, 'rdapify-pro.linux-x64-gnu.node')
185
+ )
186
+ try {
187
+ if (localFileExisted) {
188
+ nativeBinding = require('./rdapify-pro.linux-x64-gnu.node')
189
+ } else {
190
+ nativeBinding = require('@rdapify/pro-linux-x64-gnu')
191
+ }
192
+ } catch (e) {
193
+ loadError = e
194
+ }
195
+ }
196
+ break
197
+ case 'arm64':
198
+ if (isMusl()) {
199
+ localFileExisted = existsSync(
200
+ join(__dirname, 'rdapify-pro.linux-arm64-musl.node')
201
+ )
202
+ try {
203
+ if (localFileExisted) {
204
+ nativeBinding = require('./rdapify-pro.linux-arm64-musl.node')
205
+ } else {
206
+ nativeBinding = require('@rdapify/pro-linux-arm64-musl')
207
+ }
208
+ } catch (e) {
209
+ loadError = e
210
+ }
211
+ } else {
212
+ localFileExisted = existsSync(
213
+ join(__dirname, 'rdapify-pro.linux-arm64-gnu.node')
214
+ )
215
+ try {
216
+ if (localFileExisted) {
217
+ nativeBinding = require('./rdapify-pro.linux-arm64-gnu.node')
218
+ } else {
219
+ nativeBinding = require('@rdapify/pro-linux-arm64-gnu')
220
+ }
221
+ } catch (e) {
222
+ loadError = e
223
+ }
224
+ }
225
+ break
226
+ case 'arm':
227
+ if (isMusl()) {
228
+ localFileExisted = existsSync(
229
+ join(__dirname, 'rdapify-pro.linux-arm-musleabihf.node')
230
+ )
231
+ try {
232
+ if (localFileExisted) {
233
+ nativeBinding = require('./rdapify-pro.linux-arm-musleabihf.node')
234
+ } else {
235
+ nativeBinding = require('@rdapify/pro-linux-arm-musleabihf')
236
+ }
237
+ } catch (e) {
238
+ loadError = e
239
+ }
240
+ } else {
241
+ localFileExisted = existsSync(
242
+ join(__dirname, 'rdapify-pro.linux-arm-gnueabihf.node')
243
+ )
244
+ try {
245
+ if (localFileExisted) {
246
+ nativeBinding = require('./rdapify-pro.linux-arm-gnueabihf.node')
247
+ } else {
248
+ nativeBinding = require('@rdapify/pro-linux-arm-gnueabihf')
249
+ }
250
+ } catch (e) {
251
+ loadError = e
252
+ }
253
+ }
254
+ break
255
+ case 'riscv64':
256
+ if (isMusl()) {
257
+ localFileExisted = existsSync(
258
+ join(__dirname, 'rdapify-pro.linux-riscv64-musl.node')
259
+ )
260
+ try {
261
+ if (localFileExisted) {
262
+ nativeBinding = require('./rdapify-pro.linux-riscv64-musl.node')
263
+ } else {
264
+ nativeBinding = require('@rdapify/pro-linux-riscv64-musl')
265
+ }
266
+ } catch (e) {
267
+ loadError = e
268
+ }
269
+ } else {
270
+ localFileExisted = existsSync(
271
+ join(__dirname, 'rdapify-pro.linux-riscv64-gnu.node')
272
+ )
273
+ try {
274
+ if (localFileExisted) {
275
+ nativeBinding = require('./rdapify-pro.linux-riscv64-gnu.node')
276
+ } else {
277
+ nativeBinding = require('@rdapify/pro-linux-riscv64-gnu')
278
+ }
279
+ } catch (e) {
280
+ loadError = e
281
+ }
282
+ }
283
+ break
284
+ case 's390x':
285
+ localFileExisted = existsSync(
286
+ join(__dirname, 'rdapify-pro.linux-s390x-gnu.node')
287
+ )
288
+ try {
289
+ if (localFileExisted) {
290
+ nativeBinding = require('./rdapify-pro.linux-s390x-gnu.node')
291
+ } else {
292
+ nativeBinding = require('@rdapify/pro-linux-s390x-gnu')
293
+ }
294
+ } catch (e) {
295
+ loadError = e
296
+ }
297
+ break
298
+ default:
299
+ throw new Error(`Unsupported architecture on Linux: ${arch}`)
300
+ }
301
+ break
302
+ default:
303
+ throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
304
+ }
305
+
306
+ if (!nativeBinding) {
307
+ if (loadError) {
308
+ throw loadError
309
+ }
310
+ throw new Error(`Failed to load native binding`)
311
+ }
312
+
313
+ const { Reporter, HistoryTracker, Exporter, WebhookManager, LicenseValidator, BulkMonitor, ChangeDetector, proPlugin } = nativeBinding
314
+
315
+ module.exports.Reporter = Reporter
316
+ module.exports.HistoryTracker = HistoryTracker
317
+ module.exports.Exporter = Exporter
318
+ module.exports.WebhookManager = WebhookManager
319
+ module.exports.LicenseValidator = LicenseValidator
320
+ module.exports.BulkMonitor = BulkMonitor
321
+ module.exports.ChangeDetector = ChangeDetector
322
+ module.exports.proPlugin = proPlugin
package/package.json ADDED
@@ -0,0 +1,98 @@
1
+ {
2
+ "name": "@rdapify/pro",
3
+ "version": "0.2.0",
4
+ "description": "Premium Rust-native features for RDAPify — Bulk Monitoring, Change Detection, Analytics & more",
5
+ "main": "wrapper.js",
6
+ "types": "index.d.ts",
7
+ "napi": {
8
+ "name": "rdapify-pro",
9
+ "triples": {
10
+ "defaults": true,
11
+ "additional": [
12
+ "aarch64-apple-darwin",
13
+ "aarch64-unknown-linux-gnu",
14
+ "aarch64-unknown-linux-musl",
15
+ "x86_64-unknown-linux-musl"
16
+ ]
17
+ }
18
+ },
19
+ "scripts": {
20
+ "build": "napi build --platform --release",
21
+ "build:debug": "napi build --platform",
22
+ "artifacts": "napi artifacts",
23
+ "prepublishOnly": "napi prepublish -t npm",
24
+ "universal": "napi universal",
25
+ "version": "napi version",
26
+ "test": "jest --runInBand",
27
+ "clean": "rm -rf *.node target/"
28
+ },
29
+ "jest": {
30
+ "preset": "ts-jest",
31
+ "testEnvironment": "node",
32
+ "testMatch": [
33
+ "**/tests/**/*.test.ts"
34
+ ],
35
+ "moduleFileExtensions": [
36
+ "ts",
37
+ "js",
38
+ "json"
39
+ ],
40
+ "transform": {
41
+ "^.+\\.ts$": [
42
+ "ts-jest",
43
+ {
44
+ "tsconfig": {
45
+ "strict": false
46
+ }
47
+ }
48
+ ]
49
+ }
50
+ },
51
+ "peerDependencies": {
52
+ "rdapify": ">=0.1.9"
53
+ },
54
+ "devDependencies": {
55
+ "@napi-rs/cli": "^2.18.0",
56
+ "rdapify": "file:../rdapify",
57
+ "jest": "^29.7.0",
58
+ "@types/jest": "^29.5.0",
59
+ "ts-jest": "^29.2.0",
60
+ "typescript": "^5.7.0",
61
+ "@types/node": "^22.0.0"
62
+ },
63
+ "optionalDependencies": {
64
+ "@rdapify/pro-win32-x64-msvc": "0.2.0",
65
+ "@rdapify/pro-darwin-x64": "0.2.0",
66
+ "@rdapify/pro-linux-x64-gnu": "0.2.0",
67
+ "@rdapify/pro-darwin-arm64": "0.2.0",
68
+ "@rdapify/pro-linux-arm64-gnu": "0.2.0",
69
+ "@rdapify/pro-linux-arm64-musl": "0.2.0",
70
+ "@rdapify/pro-linux-x64-musl": "0.2.0"
71
+ },
72
+ "files": [
73
+ "index.js",
74
+ "wrapper.js",
75
+ "index.d.ts",
76
+ "rdapify-pro.node",
77
+ "LICENSE",
78
+ "README.md"
79
+ ],
80
+ "engines": {
81
+ "node": ">=20.0.0"
82
+ },
83
+ "license": "SEE LICENSE IN LICENSE",
84
+ "repository": {
85
+ "type": "git",
86
+ "url": "https://github.com/rdapify/RDAPify-Pro.git"
87
+ },
88
+ "keywords": [
89
+ "rdap",
90
+ "rdapify",
91
+ "rust",
92
+ "native",
93
+ "monitoring",
94
+ "domain",
95
+ "whois",
96
+ "analytics"
97
+ ]
98
+ }
package/wrapper.js ADDED
@@ -0,0 +1,90 @@
1
+ 'use strict';
2
+
3
+ const native = require('./index.js');
4
+
5
+ const {
6
+ Reporter,
7
+ HistoryTracker,
8
+ Exporter,
9
+ WebhookManager,
10
+ LicenseValidator,
11
+ BulkMonitor,
12
+ ChangeDetector,
13
+ proPlugin,
14
+ } = native;
15
+
16
+ module.exports.Reporter = Reporter;
17
+ module.exports.HistoryTracker = HistoryTracker;
18
+ module.exports.Exporter = Exporter;
19
+ module.exports.WebhookManager = WebhookManager;
20
+ module.exports.LicenseValidator = LicenseValidator;
21
+ module.exports.BulkMonitor = BulkMonitor;
22
+ module.exports.ChangeDetector = ChangeDetector;
23
+ module.exports.proPlugin = proPlugin;
24
+
25
+ /**
26
+ * ProPlugin — synchronous factory that validates a license key offline and
27
+ * returns a plugin descriptor with an `install(client)` method.
28
+ *
29
+ * @param {object} options
30
+ * @param {string} options.licenseKey
31
+ * @param {boolean} [options.onlineValidation=false]
32
+ */
33
+ function ProPlugin(options) {
34
+ if (!options || !options.licenseKey) {
35
+ throw new Error(
36
+ '[@rdapify/pro] License key required. Get one at https://rdapify.dev/pricing'
37
+ );
38
+ }
39
+
40
+ const license = LicenseValidator.validate(options.licenseKey);
41
+ if (!license.valid) {
42
+ throw new Error(
43
+ `[@rdapify/pro] Invalid or expired license key. Reason: ${license.reason}`
44
+ );
45
+ }
46
+
47
+ return {
48
+ name: '@rdapify/pro',
49
+ version: require('./package.json').version,
50
+ license,
51
+ install(client) {
52
+ const history = new HistoryTracker();
53
+
54
+ client.monitor = function (targets, opts) {
55
+ return new BulkMonitor(
56
+ targets,
57
+ (opts && opts.interval) || undefined,
58
+ (opts && opts.concurrency) || undefined
59
+ );
60
+ };
61
+
62
+ client.detect = function (targets) {
63
+ return new ChangeDetector(targets);
64
+ };
65
+
66
+ client.report = function () {
67
+ return new Reporter();
68
+ };
69
+
70
+ client.history = history;
71
+ client.webhooks = new WebhookManager();
72
+ client.export = new Exporter();
73
+
74
+ if (typeof client.use === 'function') {
75
+ client.use({
76
+ afterQuery(ctx) {
77
+ const result = ctx.result;
78
+ if (result) {
79
+ const queryType = ctx.queryType != null ? String(ctx.queryType) : 'domain';
80
+ const query = ctx.query != null ? String(ctx.query) : '';
81
+ history.record(query, queryType, JSON.stringify(result));
82
+ }
83
+ },
84
+ });
85
+ }
86
+ },
87
+ };
88
+ }
89
+
90
+ module.exports.ProPlugin = ProPlugin;