@digitaldefiance/node-express-suite 3.12.11 → 3.12.13

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 (41) hide show
  1. package/package.json +6 -3
  2. package/src/interfaces/index.d.ts +1 -0
  3. package/src/interfaces/index.d.ts.map +1 -1
  4. package/src/interfaces/index.js +1 -0
  5. package/src/interfaces/index.js.map +1 -1
  6. package/src/interfaces/network/index.d.ts +3 -0
  7. package/src/interfaces/network/index.d.ts.map +1 -0
  8. package/src/interfaces/network/index.js +6 -0
  9. package/src/interfaces/network/index.js.map +1 -0
  10. package/src/interfaces/network/upnpService.d.ts +86 -0
  11. package/src/interfaces/network/upnpService.d.ts.map +1 -0
  12. package/src/interfaces/network/upnpService.js +3 -0
  13. package/src/interfaces/network/upnpService.js.map +1 -0
  14. package/src/interfaces/network/upnpTypes.d.ts +120 -0
  15. package/src/interfaces/network/upnpTypes.d.ts.map +1 -0
  16. package/src/interfaces/network/upnpTypes.js +57 -0
  17. package/src/interfaces/network/upnpTypes.js.map +1 -0
  18. package/src/plugins/index.d.ts +1 -0
  19. package/src/plugins/index.d.ts.map +1 -1
  20. package/src/plugins/index.js +1 -0
  21. package/src/plugins/index.js.map +1 -1
  22. package/src/plugins/upnp.d.ts +129 -0
  23. package/src/plugins/upnp.d.ts.map +1 -0
  24. package/src/plugins/upnp.js +158 -0
  25. package/src/plugins/upnp.js.map +1 -0
  26. package/src/services/index.d.ts +3 -0
  27. package/src/services/index.d.ts.map +1 -1
  28. package/src/services/index.js +3 -0
  29. package/src/services/index.js.map +1 -1
  30. package/src/services/upnp-config.d.ts +131 -0
  31. package/src/services/upnp-config.d.ts.map +1 -0
  32. package/src/services/upnp-config.js +225 -0
  33. package/src/services/upnp-config.js.map +1 -0
  34. package/src/services/upnp-manager.d.ts +211 -0
  35. package/src/services/upnp-manager.d.ts.map +1 -0
  36. package/src/services/upnp-manager.js +447 -0
  37. package/src/services/upnp-manager.js.map +1 -0
  38. package/src/services/upnp.d.ts +241 -0
  39. package/src/services/upnp.d.ts.map +1 -0
  40. package/src/services/upnp.js +415 -0
  41. package/src/services/upnp.js.map +1 -0
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ /**
3
+ * UPnP Plugin for the express-suite plugin system.
4
+ *
5
+ * Integrates the UPnP port mapping lifecycle (UpnpManager) with the
6
+ * application plugin system. Register this plugin to automatically
7
+ * manage UPnP port mappings during application startup and shutdown.
8
+ *
9
+ * Requirements: 5.1, 5.2, 5.3, 5.4, 5.5
10
+ *
11
+ * @module plugins/upnp
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { UpnpPlugin } from '@digitaldefiance/node-express-suite';
16
+ *
17
+ * // Register with defaults (reads config from environment)
18
+ * app.plugins.register(new UpnpPlugin());
19
+ *
20
+ * // Register with config overrides
21
+ * app.plugins.register(new UpnpPlugin({
22
+ * config: { enabled: true, httpPort: 8080 },
23
+ * descriptionPrefix: 'My App',
24
+ * }));
25
+ * ```
26
+ */
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.UpnpPlugin = void 0;
29
+ const upnp_config_1 = require("../services/upnp-config");
30
+ const upnp_manager_1 = require("../services/upnp-manager");
31
+ /**
32
+ * UPnP plugin implementing {@link IApplicationPlugin}.
33
+ *
34
+ * Manages UPnP port mapping lifecycle through the express-suite plugin system.
35
+ * On `init`, reads configuration (from environment or overrides) and starts
36
+ * the UpnpManager. On `stop`, shuts down the manager and removes mappings.
37
+ *
38
+ * @template TID - Platform ID type (defaults to Buffer)
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const plugin = new UpnpPlugin({ descriptionPrefix: 'My App' });
43
+ * pluginManager.register(plugin);
44
+ *
45
+ * // After init, access the manager for endpoint queries
46
+ * const endpoints = await plugin.getManager()?.getExternalEndpoints();
47
+ * ```
48
+ */
49
+ class UpnpPlugin {
50
+ name = 'upnp';
51
+ version = '1.0.0';
52
+ manager = null;
53
+ options;
54
+ /**
55
+ * Create a new UpnpPlugin.
56
+ *
57
+ * @param options - Optional plugin configuration
58
+ */
59
+ constructor(options) {
60
+ this.options = options ?? {};
61
+ }
62
+ /**
63
+ * Initialize the UPnP plugin.
64
+ *
65
+ * Builds configuration from environment variables (with optional overrides),
66
+ * creates a UpnpManager, and initializes port mappings.
67
+ *
68
+ * If UPnP is disabled in the configuration, skips initialization and logs
69
+ * a message.
70
+ *
71
+ * **Validates: Requirements 5.2, 5.4, 5.5**
72
+ *
73
+ * @param _app - The application instance (unused, config comes from env)
74
+ */
75
+ async init(_app) {
76
+ // Build config: use override if provided, else load from environment
77
+ const config = this.options.config
78
+ ? upnp_config_1.UpnpConfig.fromEnvironment({
79
+ ...process.env,
80
+ ...this.envOverridesFromConfig(this.options.config),
81
+ })
82
+ : upnp_config_1.UpnpConfig.fromEnvironment();
83
+ if (!config.enabled) {
84
+ console.log('[UPnP Plugin] UPnP is disabled, skipping initialization');
85
+ return;
86
+ }
87
+ this.manager = new upnp_manager_1.UpnpManager({
88
+ config,
89
+ descriptionPrefix: this.options.descriptionPrefix,
90
+ });
91
+ await this.manager.initialize();
92
+ }
93
+ /**
94
+ * Stop the UPnP plugin.
95
+ *
96
+ * Shuts down the UpnpManager, removing all port mappings and cleaning up.
97
+ *
98
+ * **Validates: Requirement 5.3**
99
+ */
100
+ async stop() {
101
+ if (this.manager) {
102
+ await this.manager.shutdown();
103
+ this.manager = null;
104
+ }
105
+ }
106
+ /**
107
+ * Get the underlying UpnpManager instance.
108
+ *
109
+ * Returns `null` if the plugin has not been initialized or UPnP is disabled.
110
+ * Use this to query external endpoints or inspect manager state.
111
+ *
112
+ * @returns The UpnpManager instance, or `null` if not initialized
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * const manager = plugin.getManager();
117
+ * if (manager) {
118
+ * const endpoints = await manager.getExternalEndpoints();
119
+ * console.log(endpoints);
120
+ * }
121
+ * ```
122
+ */
123
+ getManager() {
124
+ return this.manager;
125
+ }
126
+ /**
127
+ * Convert a partial IUpnpConfig into environment variable overrides.
128
+ *
129
+ * Maps each defined config field to its corresponding `UPNP_*` environment
130
+ * variable name, converting values to strings. These overrides are merged
131
+ * with `process.env` before passing to `UpnpConfig.fromEnvironment`.
132
+ *
133
+ * @param config - Partial configuration with fields to override
134
+ * @returns Record of environment variable name → string value
135
+ */
136
+ envOverridesFromConfig(config) {
137
+ const env = {};
138
+ if (config.enabled !== undefined)
139
+ env['UPNP_ENABLED'] = String(config.enabled);
140
+ if (config.httpPort !== undefined)
141
+ env['UPNP_HTTP_PORT'] = String(config.httpPort);
142
+ if (config.websocketPort !== undefined)
143
+ env['UPNP_WEBSOCKET_PORT'] = String(config.websocketPort);
144
+ if (config.ttl !== undefined)
145
+ env['UPNP_TTL'] = String(config.ttl);
146
+ if (config.refreshInterval !== undefined)
147
+ env['UPNP_REFRESH_INTERVAL'] = String(config.refreshInterval);
148
+ if (config.protocol !== undefined)
149
+ env['UPNP_PROTOCOL'] = config.protocol;
150
+ if (config.retryAttempts !== undefined)
151
+ env['UPNP_RETRY_ATTEMPTS'] = String(config.retryAttempts);
152
+ if (config.retryDelay !== undefined)
153
+ env['UPNP_RETRY_DELAY'] = String(config.retryDelay);
154
+ return env;
155
+ }
156
+ }
157
+ exports.UpnpPlugin = UpnpPlugin;
158
+ //# sourceMappingURL=upnp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upnp.js","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-node-express-suite/src/plugins/upnp.ts"],"names":[],"mappings":"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"}
@@ -16,4 +16,7 @@ export * from './symmetric';
16
16
  export * from './system-user';
17
17
  export * from './user';
18
18
  export * from './xor';
19
+ export * from './upnp';
20
+ export * from './upnp-config';
21
+ export * from './upnp-manager';
19
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-node-express-suite/src/services/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,OAAO,CAAC;AACtB,cAAc,qBAAqB,CAAC;AACpC,cAAc,OAAO,CAAC;AACtB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-node-express-suite/src/services/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,OAAO,CAAC;AACtB,cAAc,qBAAqB,CAAC;AACpC,cAAc,OAAO,CAAC;AACtB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,QAAQ,CAAC;AACvB,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC"}
@@ -19,4 +19,7 @@ tslib_1.__exportStar(require("./symmetric"), exports);
19
19
  tslib_1.__exportStar(require("./system-user"), exports);
20
20
  tslib_1.__exportStar(require("./user"), exports);
21
21
  tslib_1.__exportStar(require("./xor"), exports);
22
+ tslib_1.__exportStar(require("./upnp"), exports);
23
+ tslib_1.__exportStar(require("./upnp-config"), exports);
24
+ tslib_1.__exportStar(require("./upnp-manager"), exports);
22
25
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-node-express-suite/src/services/index.ts"],"names":[],"mappings":";;;AAAA,wDAA8B;AAC9B,iDAAuB;AACvB,qDAA2B;AAC3B,oEAA0C;AAC1C,0DAAgC;AAChC,+DAAqC;AACrC,gEAAsC;AACtC,gDAAsB;AACtB,8DAAoC;AACpC,gDAAsB;AACtB,yDAA+B;AAC/B,qDAA2B;AAC3B,yDAA+B;AAC/B,iDAAuB;AACvB,sDAA4B;AAC5B,wDAA8B;AAC9B,iDAAuB;AACvB,gDAAsB"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-node-express-suite/src/services/index.ts"],"names":[],"mappings":";;;AAAA,wDAA8B;AAC9B,iDAAuB;AACvB,qDAA2B;AAC3B,oEAA0C;AAC1C,0DAAgC;AAChC,+DAAqC;AACrC,gEAAsC;AACtC,gDAAsB;AACtB,8DAAoC;AACpC,gDAAsB;AACtB,yDAA+B;AAC/B,qDAA2B;AAC3B,yDAA+B;AAC/B,iDAAuB;AACvB,sDAA4B;AAC5B,wDAA8B;AAC9B,iDAAuB;AACvB,gDAAsB;AACtB,iDAAuB;AACvB,wDAA8B;AAC9B,yDAA+B"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * UPnP Configuration Loader and Validator.
3
+ *
4
+ * Reads UPnP settings from environment variables, applies defaults
5
+ * for missing values, and validates all configuration parameters.
6
+ * Use the static {@link UpnpConfig.fromEnvironment} factory method
7
+ * to create validated instances.
8
+ *
9
+ * Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9
10
+ */
11
+ import { IUpnpConfig, UpnpProtocol } from '../interfaces/network/upnpTypes';
12
+ /**
13
+ * Error thrown when UPnP configuration validation fails.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * try {
18
+ * const config = UpnpConfig.fromEnvironment({ UPNP_HTTP_PORT: '99999' });
19
+ * } catch (err) {
20
+ * if (err instanceof UpnpConfigValidationError) {
21
+ * console.error(`Invalid config: ${err.message}`);
22
+ * }
23
+ * }
24
+ * ```
25
+ */
26
+ export declare class UpnpConfigValidationError extends Error {
27
+ /**
28
+ * @param message - Description of the validation failure
29
+ */
30
+ constructor(message: string);
31
+ }
32
+ /**
33
+ * UPnP configuration loaded from environment variables.
34
+ *
35
+ * Reads `UPNP_*` env vars, falls back to {@link UPNP_CONFIG_DEFAULTS},
36
+ * and validates all values on construction. Use the static
37
+ * {@link UpnpConfig.fromEnvironment} factory method to create instances.
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * // Load from process.env (production usage)
42
+ * const config = UpnpConfig.fromEnvironment();
43
+ *
44
+ * // Load from custom env (testing)
45
+ * const testConfig = UpnpConfig.fromEnvironment({
46
+ * UPNP_ENABLED: 'true',
47
+ * UPNP_HTTP_PORT: '8080',
48
+ * });
49
+ *
50
+ * console.log(config.enabled); // false (default)
51
+ * console.log(config.httpPort); // 3000 (default)
52
+ * ```
53
+ */
54
+ export declare class UpnpConfig implements IUpnpConfig {
55
+ /** Whether UPnP is enabled */
56
+ readonly enabled: boolean;
57
+ /** HTTP/Express port to map externally */
58
+ readonly httpPort: number;
59
+ /** WebSocket port to map externally */
60
+ readonly websocketPort: number;
61
+ /** Mapping time-to-live in seconds */
62
+ readonly ttl: number;
63
+ /** Refresh interval in milliseconds */
64
+ readonly refreshInterval: number;
65
+ /** UPnP protocol to use (upnp, natpmp, or auto) */
66
+ readonly protocol: UpnpProtocol;
67
+ /** Number of retry attempts for failed operations */
68
+ readonly retryAttempts: number;
69
+ /** Delay between retries in milliseconds */
70
+ readonly retryDelay: number;
71
+ /**
72
+ * Private constructor — use {@link UpnpConfig.fromEnvironment} to create instances.
73
+ *
74
+ * @param config - Validated configuration values
75
+ */
76
+ private constructor();
77
+ /**
78
+ * Create an UpnpConfig from the current process environment.
79
+ * Missing variables fall back to {@link UPNP_CONFIG_DEFAULTS}.
80
+ *
81
+ * @param env - Environment variable map (defaults to `process.env`)
82
+ * @returns A validated UpnpConfig instance
83
+ * @throws {UpnpConfigValidationError} If any value is invalid
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * // Use process.env
88
+ * const config = UpnpConfig.fromEnvironment();
89
+ *
90
+ * // Use custom env for testing
91
+ * const config = UpnpConfig.fromEnvironment({
92
+ * UPNP_ENABLED: 'true',
93
+ * UPNP_HTTP_PORT: '8080',
94
+ * UPNP_TTL: '7200',
95
+ * });
96
+ * ```
97
+ */
98
+ static fromEnvironment(env?: Record<string, string | undefined>): UpnpConfig;
99
+ /**
100
+ * Validate all configuration values. Throws on the first invalid value.
101
+ *
102
+ * @param config - The configuration object to validate
103
+ * @throws {UpnpConfigValidationError} If any value is outside its valid range:
104
+ * - `httpPort` / `websocketPort`: must be 1–65535
105
+ * - `ttl`: must be 60–86400 seconds (24 hours max)
106
+ * - `refreshInterval`: must be positive and less than `ttl * 1000`
107
+ * - `retryAttempts`: must be 1–10
108
+ * - `retryDelay`: must be 1000–60000 ms
109
+ * - `protocol`: must be a valid {@link UpnpProtocol} value
110
+ */
111
+ static validate(config: IUpnpConfig): void;
112
+ /**
113
+ * Parse a string environment variable as an integer.
114
+ *
115
+ * @param value - The raw environment variable value
116
+ * @param fallback - Default value when the variable is undefined or empty
117
+ * @returns The parsed integer or the fallback value
118
+ * @throws {UpnpConfigValidationError} If the value is not a valid integer
119
+ */
120
+ private static parseIntEnv;
121
+ /**
122
+ * Parse a string environment variable as a {@link UpnpProtocol} value.
123
+ *
124
+ * @param value - The raw environment variable value
125
+ * @param fallback - Default protocol when the variable is undefined or empty
126
+ * @returns The parsed protocol or the fallback value
127
+ * @throws {UpnpConfigValidationError} If the value is not a valid protocol
128
+ */
129
+ private static parseProtocol;
130
+ }
131
+ //# sourceMappingURL=upnp-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upnp-config.d.ts","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-node-express-suite/src/services/upnp-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,WAAW,EAEX,YAAY,EACb,MAAM,iCAAiC,CAAC;AAEzC;;;;;;;;;;;;;GAaG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;IAClD;;OAEG;gBACS,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,UAAW,YAAW,WAAW;IAC5C,8BAA8B;IAC9B,SAAgB,OAAO,EAAE,OAAO,CAAC;IACjC,0CAA0C;IAC1C,SAAgB,QAAQ,EAAE,MAAM,CAAC;IACjC,uCAAuC;IACvC,SAAgB,aAAa,EAAE,MAAM,CAAC;IACtC,sCAAsC;IACtC,SAAgB,GAAG,EAAE,MAAM,CAAC;IAC5B,uCAAuC;IACvC,SAAgB,eAAe,EAAE,MAAM,CAAC;IACxC,mDAAmD;IACnD,SAAgB,QAAQ,EAAE,YAAY,CAAC;IACvC,qDAAqD;IACrD,SAAgB,aAAa,EAAE,MAAM,CAAC;IACtC,4CAA4C;IAC5C,SAAgB,UAAU,EAAE,MAAM,CAAC;IAEnC;;;;OAIG;IACH,OAAO;IAWP;;;;;;;;;;;;;;;;;;;;OAoBG;WACW,eAAe,CAC3B,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,UAAU;IAqCb;;;;;;;;;;;OAWG;WACW,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IA8EjD;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAgB1B;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;CAgB7B"}
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ /**
3
+ * UPnP Configuration Loader and Validator.
4
+ *
5
+ * Reads UPnP settings from environment variables, applies defaults
6
+ * for missing values, and validates all configuration parameters.
7
+ * Use the static {@link UpnpConfig.fromEnvironment} factory method
8
+ * to create validated instances.
9
+ *
10
+ * Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.UpnpConfig = exports.UpnpConfigValidationError = void 0;
14
+ const upnpTypes_1 = require("../interfaces/network/upnpTypes");
15
+ /**
16
+ * Error thrown when UPnP configuration validation fails.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * try {
21
+ * const config = UpnpConfig.fromEnvironment({ UPNP_HTTP_PORT: '99999' });
22
+ * } catch (err) {
23
+ * if (err instanceof UpnpConfigValidationError) {
24
+ * console.error(`Invalid config: ${err.message}`);
25
+ * }
26
+ * }
27
+ * ```
28
+ */
29
+ class UpnpConfigValidationError extends Error {
30
+ /**
31
+ * @param message - Description of the validation failure
32
+ */
33
+ constructor(message) {
34
+ super(message);
35
+ this.name = 'UpnpConfigValidationError';
36
+ }
37
+ }
38
+ exports.UpnpConfigValidationError = UpnpConfigValidationError;
39
+ /**
40
+ * UPnP configuration loaded from environment variables.
41
+ *
42
+ * Reads `UPNP_*` env vars, falls back to {@link UPNP_CONFIG_DEFAULTS},
43
+ * and validates all values on construction. Use the static
44
+ * {@link UpnpConfig.fromEnvironment} factory method to create instances.
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * // Load from process.env (production usage)
49
+ * const config = UpnpConfig.fromEnvironment();
50
+ *
51
+ * // Load from custom env (testing)
52
+ * const testConfig = UpnpConfig.fromEnvironment({
53
+ * UPNP_ENABLED: 'true',
54
+ * UPNP_HTTP_PORT: '8080',
55
+ * });
56
+ *
57
+ * console.log(config.enabled); // false (default)
58
+ * console.log(config.httpPort); // 3000 (default)
59
+ * ```
60
+ */
61
+ class UpnpConfig {
62
+ /** Whether UPnP is enabled */
63
+ enabled;
64
+ /** HTTP/Express port to map externally */
65
+ httpPort;
66
+ /** WebSocket port to map externally */
67
+ websocketPort;
68
+ /** Mapping time-to-live in seconds */
69
+ ttl;
70
+ /** Refresh interval in milliseconds */
71
+ refreshInterval;
72
+ /** UPnP protocol to use (upnp, natpmp, or auto) */
73
+ protocol;
74
+ /** Number of retry attempts for failed operations */
75
+ retryAttempts;
76
+ /** Delay between retries in milliseconds */
77
+ retryDelay;
78
+ /**
79
+ * Private constructor — use {@link UpnpConfig.fromEnvironment} to create instances.
80
+ *
81
+ * @param config - Validated configuration values
82
+ */
83
+ constructor(config) {
84
+ this.enabled = config.enabled;
85
+ this.httpPort = config.httpPort;
86
+ this.websocketPort = config.websocketPort;
87
+ this.ttl = config.ttl;
88
+ this.refreshInterval = config.refreshInterval;
89
+ this.protocol = config.protocol;
90
+ this.retryAttempts = config.retryAttempts;
91
+ this.retryDelay = config.retryDelay;
92
+ }
93
+ /**
94
+ * Create an UpnpConfig from the current process environment.
95
+ * Missing variables fall back to {@link UPNP_CONFIG_DEFAULTS}.
96
+ *
97
+ * @param env - Environment variable map (defaults to `process.env`)
98
+ * @returns A validated UpnpConfig instance
99
+ * @throws {UpnpConfigValidationError} If any value is invalid
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * // Use process.env
104
+ * const config = UpnpConfig.fromEnvironment();
105
+ *
106
+ * // Use custom env for testing
107
+ * const config = UpnpConfig.fromEnvironment({
108
+ * UPNP_ENABLED: 'true',
109
+ * UPNP_HTTP_PORT: '8080',
110
+ * UPNP_TTL: '7200',
111
+ * });
112
+ * ```
113
+ */
114
+ static fromEnvironment(env = process.env) {
115
+ const config = {
116
+ enabled: env['UPNP_ENABLED'] !== undefined
117
+ ? env['UPNP_ENABLED'].toLowerCase() === 'true'
118
+ : upnpTypes_1.UPNP_CONFIG_DEFAULTS.enabled,
119
+ httpPort: UpnpConfig.parseIntEnv(env['UPNP_HTTP_PORT'], upnpTypes_1.UPNP_CONFIG_DEFAULTS.httpPort),
120
+ websocketPort: UpnpConfig.parseIntEnv(env['UPNP_WEBSOCKET_PORT'], upnpTypes_1.UPNP_CONFIG_DEFAULTS.websocketPort),
121
+ ttl: UpnpConfig.parseIntEnv(env['UPNP_TTL'], upnpTypes_1.UPNP_CONFIG_DEFAULTS.ttl),
122
+ refreshInterval: UpnpConfig.parseIntEnv(env['UPNP_REFRESH_INTERVAL'], upnpTypes_1.UPNP_CONFIG_DEFAULTS.refreshInterval),
123
+ protocol: UpnpConfig.parseProtocol(env['UPNP_PROTOCOL'], upnpTypes_1.UPNP_CONFIG_DEFAULTS.protocol),
124
+ retryAttempts: UpnpConfig.parseIntEnv(env['UPNP_RETRY_ATTEMPTS'], upnpTypes_1.UPNP_CONFIG_DEFAULTS.retryAttempts),
125
+ retryDelay: UpnpConfig.parseIntEnv(env['UPNP_RETRY_DELAY'], upnpTypes_1.UPNP_CONFIG_DEFAULTS.retryDelay),
126
+ };
127
+ UpnpConfig.validate(config);
128
+ return new UpnpConfig(config);
129
+ }
130
+ /**
131
+ * Validate all configuration values. Throws on the first invalid value.
132
+ *
133
+ * @param config - The configuration object to validate
134
+ * @throws {UpnpConfigValidationError} If any value is outside its valid range:
135
+ * - `httpPort` / `websocketPort`: must be 1–65535
136
+ * - `ttl`: must be 60–86400 seconds (24 hours max)
137
+ * - `refreshInterval`: must be positive and less than `ttl * 1000`
138
+ * - `retryAttempts`: must be 1–10
139
+ * - `retryDelay`: must be 1000–60000 ms
140
+ * - `protocol`: must be a valid {@link UpnpProtocol} value
141
+ */
142
+ static validate(config) {
143
+ // Port validation (1–65535)
144
+ if (!Number.isInteger(config.httpPort) ||
145
+ config.httpPort < 1 ||
146
+ config.httpPort > 65535) {
147
+ throw new UpnpConfigValidationError(`httpPort must be an integer between 1 and 65535, got ${config.httpPort}`);
148
+ }
149
+ if (!Number.isInteger(config.websocketPort) ||
150
+ config.websocketPort < 1 ||
151
+ config.websocketPort > 65535) {
152
+ throw new UpnpConfigValidationError(`websocketPort must be an integer between 1 and 65535, got ${config.websocketPort}`);
153
+ }
154
+ // TTL validation (minimum 60 seconds, maximum 86400 seconds / 24 hours)
155
+ if (!Number.isInteger(config.ttl) ||
156
+ config.ttl < 60 ||
157
+ config.ttl > 86400) {
158
+ throw new UpnpConfigValidationError(`ttl must be an integer between 60 and 86400 seconds (24 hours), got ${config.ttl}`);
159
+ }
160
+ // Refresh interval must be less than TTL * 1000 (TTL is seconds, refresh is ms)
161
+ if (!Number.isInteger(config.refreshInterval) ||
162
+ config.refreshInterval <= 0) {
163
+ throw new UpnpConfigValidationError(`refreshInterval must be a positive integer, got ${config.refreshInterval}`);
164
+ }
165
+ if (config.refreshInterval >= config.ttl * 1000) {
166
+ throw new UpnpConfigValidationError(`refreshInterval (${config.refreshInterval}ms) must be less than ttl * 1000 (${config.ttl * 1000}ms)`);
167
+ }
168
+ // Retry attempts (1–10)
169
+ if (!Number.isInteger(config.retryAttempts) ||
170
+ config.retryAttempts < 1 ||
171
+ config.retryAttempts > 10) {
172
+ throw new UpnpConfigValidationError(`retryAttempts must be an integer between 1 and 10, got ${config.retryAttempts}`);
173
+ }
174
+ // Retry delay (1000–60000ms)
175
+ if (!Number.isInteger(config.retryDelay) ||
176
+ config.retryDelay < 1000 ||
177
+ config.retryDelay > 60000) {
178
+ throw new UpnpConfigValidationError(`retryDelay must be an integer between 1000 and 60000ms, got ${config.retryDelay}`);
179
+ }
180
+ // Protocol validation
181
+ const validProtocols = Object.values(upnpTypes_1.UpnpProtocol);
182
+ if (!validProtocols.includes(config.protocol)) {
183
+ throw new UpnpConfigValidationError(`protocol must be one of ${validProtocols.join(', ')}, got ${config.protocol}`);
184
+ }
185
+ }
186
+ /**
187
+ * Parse a string environment variable as an integer.
188
+ *
189
+ * @param value - The raw environment variable value
190
+ * @param fallback - Default value when the variable is undefined or empty
191
+ * @returns The parsed integer or the fallback value
192
+ * @throws {UpnpConfigValidationError} If the value is not a valid integer
193
+ */
194
+ static parseIntEnv(value, fallback) {
195
+ if (value === undefined || value === '') {
196
+ return fallback;
197
+ }
198
+ const parsed = Number.parseInt(value, 10);
199
+ if (Number.isNaN(parsed)) {
200
+ throw new UpnpConfigValidationError(`Expected integer value, got "${value}"`);
201
+ }
202
+ return parsed;
203
+ }
204
+ /**
205
+ * Parse a string environment variable as a {@link UpnpProtocol} value.
206
+ *
207
+ * @param value - The raw environment variable value
208
+ * @param fallback - Default protocol when the variable is undefined or empty
209
+ * @returns The parsed protocol or the fallback value
210
+ * @throws {UpnpConfigValidationError} If the value is not a valid protocol
211
+ */
212
+ static parseProtocol(value, fallback) {
213
+ if (value === undefined || value === '') {
214
+ return fallback;
215
+ }
216
+ const lower = value.toLowerCase();
217
+ const validProtocols = Object.values(upnpTypes_1.UpnpProtocol);
218
+ if (!validProtocols.includes(lower)) {
219
+ throw new UpnpConfigValidationError(`protocol must be one of ${validProtocols.join(', ')}, got "${value}"`);
220
+ }
221
+ return lower;
222
+ }
223
+ }
224
+ exports.UpnpConfig = UpnpConfig;
225
+ //# sourceMappingURL=upnp-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upnp-config.js","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-node-express-suite/src/services/upnp-config.ts"],"names":[],"mappings":"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"}