node-ipconfig 1.0.0 → 1.0.1

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/src/index.js CHANGED
@@ -1,244 +1,244 @@
1
- import { hostname as systemHostname } from "node:os";
2
- import { createRunner, IpconfigError } from "./command.js";
3
- import { parseIpconfig } from "./parser.js";
4
- import { formatConfig } from "./format.js";
5
-
6
- const EXECUTION_OPTIONS = new Set([
7
- "timeout",
8
- "signal",
9
- "encoding",
10
- "rawResult",
11
- ]);
12
- const QUERY_OPTIONS = new Set([...EXECUTION_OPTIONS, "all"]);
13
- const CONFIG_OPTIONS = new Set([...QUERY_OPTIONS, "format"]);
14
- const COMMANDS = Object.freeze({
15
- config: [],
16
- all: ["/all"],
17
- release: ["/release"],
18
- release6: ["/release6"],
19
- renew: ["/renew"],
20
- renew6: ["/renew6"],
21
- flushdns: ["/flushdns"],
22
- displaydns: ["/displaydns"],
23
- registerdns: ["/registerdns"],
24
- showclassid: ["/showclassid"],
25
- setclassid: ["/setclassid"],
26
- showclassid6: ["/showclassid6"],
27
- setclassid6: ["/setclassid6"],
28
- });
29
-
30
- function validateOptions(options, allowed) {
31
- if (options === undefined) options = {};
32
- if (!options || typeof options !== "object" || Array.isArray(options))
33
- throw new TypeError("options must be an object");
34
- for (const key of Object.keys(options))
35
- if (!allowed.has(key)) throw new TypeError(`Unsupported option: ${key}`);
36
- if (
37
- options.timeout !== undefined &&
38
- (!Number.isInteger(options.timeout) || options.timeout <= 0)
39
- )
40
- throw new TypeError("timeout must be a positive integer");
41
- if (
42
- options.signal !== undefined &&
43
- (typeof options.signal !== "object" ||
44
- typeof options.signal.aborted !== "boolean")
45
- )
46
- throw new TypeError("signal must be an AbortSignal");
47
- if (options.encoding !== undefined && typeof options.encoding !== "string")
48
- throw new TypeError("encoding must be a supported Node.js encoding name");
49
- if (options.rawResult !== undefined && typeof options.rawResult !== "boolean")
50
- throw new TypeError("rawResult must be a boolean");
51
- return options;
52
- }
53
-
54
- function queryOptions(options) {
55
- validateOptions(options, QUERY_OPTIONS);
56
- if (options.all !== undefined && typeof options.all !== "boolean")
57
- throw new TypeError("all must be a boolean");
58
- const { all, ...execution } = options;
59
- return {
60
- args: all ? ["/all"] : [],
61
- execution: { ...execution, rawResult: false },
62
- };
63
- }
64
-
65
- function adapterArgument(value, label = "adapter") {
66
- if (
67
- typeof value !== "string" ||
68
- !value.trim() ||
69
- value.length > 256 ||
70
- /[\0\r\n]/.test(value) ||
71
- value.trim().startsWith("/")
72
- ) {
73
- throw new TypeError(
74
- `${label} must be a non-empty adapter name without control characters or option prefixes`,
75
- );
76
- }
77
- return value.trim();
78
- }
79
- function classIdArgument(value) {
80
- if (
81
- typeof value !== "string" ||
82
- value.length > 256 ||
83
- /[\0\r\n]/.test(value) ||
84
- value.startsWith("/")
85
- ) {
86
- throw new TypeError(
87
- "classId must be a string without control characters or option prefixes",
88
- );
89
- }
90
- return value;
91
- }
92
- function commandOptions(options, command) {
93
- const adapterAllowed = [
94
- "release",
95
- "release6",
96
- "renew",
97
- "renew6",
98
- "showclassid",
99
- "setclassid",
100
- "showclassid6",
101
- "setclassid6",
102
- ].includes(command);
103
- const classIdAllowed = ["setclassid", "setclassid6"].includes(command);
104
- validateOptions(
105
- options,
106
- new Set([
107
- ...EXECUTION_OPTIONS,
108
- ...(adapterAllowed ? ["adapter"] : []),
109
- ...(classIdAllowed ? ["classId"] : []),
110
- ]),
111
- );
112
- const args = [...COMMANDS[command]];
113
- if (
114
- ["release", "release6", "renew", "renew6"].includes(command) &&
115
- options.adapter !== undefined
116
- )
117
- args.push(adapterArgument(options.adapter));
118
- if (command === "showclassid" || command === "showclassid6")
119
- args.push(adapterArgument(options.adapter));
120
- if (command === "setclassid" || command === "setclassid6") {
121
- args.push(adapterArgument(options.adapter));
122
- if (options.classId !== undefined)
123
- args.push(classIdArgument(options.classId));
124
- }
125
- const { adapter, classId, ...execution } = options;
126
- return { args, execution };
127
- }
128
-
129
- function flatten(adapters, field) {
130
- return adapters.flatMap((adapter) =>
131
- (adapter[field] ?? []).map((address) => ({
132
- adapter: adapter.name,
133
- address,
134
- })),
135
- );
136
- }
137
-
138
- export function createIpconfig({ platform = process.platform, exec } = {}) {
139
- const run = createRunner({ platform, ...(exec ? { exec } : {}) });
140
- async function execute(command, options = {}) {
141
- if (typeof command !== "string" || !Object.hasOwn(COMMANDS, command))
142
- throw new TypeError(`Unsupported ipconfig command: ${String(command)}`);
143
- const { args, execution } = commandOptions(options, command);
144
- return run(args, execution);
145
- }
146
- async function getRawOutput(options = {}) {
147
- const { args, execution } = queryOptions(options);
148
- return run(args, { ...execution, rawResult: options.rawResult ?? false });
149
- }
150
- async function getConfig(options = {}) {
151
- validateOptions(options, CONFIG_OPTIONS);
152
- const { format = "json", ...query } = options;
153
- if (!["json", "xml", "yaml", "csv", "ndjson"].includes(format))
154
- throw new TypeError(
155
- "format must be one of: json, xml, yaml, csv, ndjson",
156
- );
157
- const { args, execution } = queryOptions(query);
158
- const output = await run(args, execution);
159
- const config = parseIpconfig(output);
160
- config.hostname ??= systemHostname();
161
- return formatConfig(config, format);
162
- }
163
- async function getAdapters(options) {
164
- validateOptions(options, QUERY_OPTIONS);
165
- return (await getConfig(options)).adapters;
166
- }
167
- async function getAdapter(nameOrIndex, options) {
168
- if (!(typeof nameOrIndex === "string" || Number.isInteger(nameOrIndex)))
169
- throw new TypeError(
170
- "adapter selector must be a name or zero-based integer index",
171
- );
172
- const adapters = await getAdapters(options);
173
- const found =
174
- typeof nameOrIndex === "number"
175
- ? adapters[nameOrIndex]
176
- : adapters.find(
177
- ({ name }) =>
178
- name.toLocaleLowerCase() ===
179
- nameOrIndex.trim().toLocaleLowerCase(),
180
- );
181
- if (!found)
182
- throw new IpconfigError(`Network adapter not found: ${nameOrIndex}`, {
183
- code: "ENOADAPTER",
184
- });
185
- return found;
186
- }
187
- async function addresses(family) {
188
- return (await getAdapters({ all: true })).flatMap(
189
- (adapter) => adapter[family],
190
- );
191
- }
192
- async function release(options = {}) {
193
- validateOptions(
194
- options,
195
- new Set([...EXECUTION_OPTIONS, "adapter", "ipv6"]),
196
- );
197
- if (options.ipv6 !== undefined && typeof options.ipv6 !== "boolean")
198
- throw new TypeError("ipv6 must be a boolean");
199
- const { ipv6 = false, ...rest } = options;
200
- return execute(ipv6 ? "release6" : "release", rest);
201
- }
202
- async function renew(options = {}) {
203
- validateOptions(
204
- options,
205
- new Set([...EXECUTION_OPTIONS, "adapter", "ipv6"]),
206
- );
207
- if (options.ipv6 !== undefined && typeof options.ipv6 !== "boolean")
208
- throw new TypeError("ipv6 must be a boolean");
209
- const { ipv6 = false, ...rest } = options;
210
- return execute(ipv6 ? "renew6" : "renew", rest);
211
- }
212
- return {
213
- execute,
214
- getRawOutput,
215
- getConfig,
216
- getAdapters,
217
- getAdapter,
218
- getIPv4: async () => addresses("ipv4"),
219
- getIPv6: async () => addresses("ipv6"),
220
- getDefaultGateway: async () =>
221
- flatten(await getAdapters({ all: true }), "defaultGateways"),
222
- getDnsServers: async () =>
223
- flatten(await getAdapters({ all: true }), "dnsServers"),
224
- getMacAddresses: async () =>
225
- (await getAdapters({ all: true })).flatMap(({ name, macAddress }) =>
226
- macAddress ? [{ adapter: name, address: macAddress }] : [],
227
- ),
228
- getHostname: async () => systemHostname(),
229
- flushDns: (options = {}) => execute("flushdns", options),
230
- displayDns: (options = {}) => execute("displaydns", options),
231
- registerDns: (options = {}) => execute("registerdns", options),
232
- release,
233
- renew,
234
- showClassId: (options = {}) => execute("showclassid", options),
235
- setClassId: (options = {}) => execute("setclassid", options),
236
- showClassId6: (options = {}) => execute("showclassid6", options),
237
- setClassId6: (options = {}) => execute("setclassid6", options),
238
- getDnsCache: (options = {}) => execute("displaydns", options),
239
- };
240
- }
241
-
242
- export const ipconfig = createIpconfig();
243
- export { parseIpconfig, IpconfigError, createRunner };
244
- export default ipconfig;
1
+ import { hostname as systemHostname } from "node:os";
2
+ import { createRunner, IpconfigError } from "./command.js";
3
+ import { parseIpconfig } from "./parser.js";
4
+ import { formatConfig } from "./format.js";
5
+
6
+ const EXECUTION_OPTIONS = new Set([
7
+ "timeout",
8
+ "signal",
9
+ "encoding",
10
+ "rawResult",
11
+ ]);
12
+ const QUERY_OPTIONS = new Set([...EXECUTION_OPTIONS, "all"]);
13
+ const CONFIG_OPTIONS = new Set([...QUERY_OPTIONS, "format"]);
14
+ const COMMANDS = Object.freeze({
15
+ config: [],
16
+ all: ["/all"],
17
+ release: ["/release"],
18
+ release6: ["/release6"],
19
+ renew: ["/renew"],
20
+ renew6: ["/renew6"],
21
+ flushdns: ["/flushdns"],
22
+ displaydns: ["/displaydns"],
23
+ registerdns: ["/registerdns"],
24
+ showclassid: ["/showclassid"],
25
+ setclassid: ["/setclassid"],
26
+ showclassid6: ["/showclassid6"],
27
+ setclassid6: ["/setclassid6"],
28
+ });
29
+
30
+ function validateOptions(options, allowed) {
31
+ if (options === undefined) options = {};
32
+ if (!options || typeof options !== "object" || Array.isArray(options))
33
+ throw new TypeError("options must be an object");
34
+ for (const key of Object.keys(options))
35
+ if (!allowed.has(key)) throw new TypeError(`Unsupported option: ${key}`);
36
+ if (
37
+ options.timeout !== undefined &&
38
+ (!Number.isInteger(options.timeout) || options.timeout <= 0)
39
+ )
40
+ throw new TypeError("timeout must be a positive integer");
41
+ if (
42
+ options.signal !== undefined &&
43
+ (typeof options.signal !== "object" ||
44
+ typeof options.signal.aborted !== "boolean")
45
+ )
46
+ throw new TypeError("signal must be an AbortSignal");
47
+ if (options.encoding !== undefined && typeof options.encoding !== "string")
48
+ throw new TypeError("encoding must be a supported Node.js encoding name");
49
+ if (options.rawResult !== undefined && typeof options.rawResult !== "boolean")
50
+ throw new TypeError("rawResult must be a boolean");
51
+ return options;
52
+ }
53
+
54
+ function queryOptions(options) {
55
+ validateOptions(options, QUERY_OPTIONS);
56
+ if (options.all !== undefined && typeof options.all !== "boolean")
57
+ throw new TypeError("all must be a boolean");
58
+ const { all, ...execution } = options;
59
+ return {
60
+ args: all ? ["/all"] : [],
61
+ execution: { ...execution, rawResult: false },
62
+ };
63
+ }
64
+
65
+ function adapterArgument(value, label = "adapter") {
66
+ if (
67
+ typeof value !== "string" ||
68
+ !value.trim() ||
69
+ value.length > 256 ||
70
+ /[\0\r\n]/.test(value) ||
71
+ value.trim().startsWith("/")
72
+ ) {
73
+ throw new TypeError(
74
+ `${label} must be a non-empty adapter name without control characters or option prefixes`,
75
+ );
76
+ }
77
+ return value.trim();
78
+ }
79
+ function classIdArgument(value) {
80
+ if (
81
+ typeof value !== "string" ||
82
+ value.length > 256 ||
83
+ /[\0\r\n]/.test(value) ||
84
+ value.startsWith("/")
85
+ ) {
86
+ throw new TypeError(
87
+ "classId must be a string without control characters or option prefixes",
88
+ );
89
+ }
90
+ return value;
91
+ }
92
+ function commandOptions(options, command) {
93
+ const adapterAllowed = [
94
+ "release",
95
+ "release6",
96
+ "renew",
97
+ "renew6",
98
+ "showclassid",
99
+ "setclassid",
100
+ "showclassid6",
101
+ "setclassid6",
102
+ ].includes(command);
103
+ const classIdAllowed = ["setclassid", "setclassid6"].includes(command);
104
+ validateOptions(
105
+ options,
106
+ new Set([
107
+ ...EXECUTION_OPTIONS,
108
+ ...(adapterAllowed ? ["adapter"] : []),
109
+ ...(classIdAllowed ? ["classId"] : []),
110
+ ]),
111
+ );
112
+ const args = [...COMMANDS[command]];
113
+ if (
114
+ ["release", "release6", "renew", "renew6"].includes(command) &&
115
+ options.adapter !== undefined
116
+ )
117
+ args.push(adapterArgument(options.adapter));
118
+ if (command === "showclassid" || command === "showclassid6")
119
+ args.push(adapterArgument(options.adapter));
120
+ if (command === "setclassid" || command === "setclassid6") {
121
+ args.push(adapterArgument(options.adapter));
122
+ if (options.classId !== undefined)
123
+ args.push(classIdArgument(options.classId));
124
+ }
125
+ const { adapter, classId, ...execution } = options;
126
+ return { args, execution };
127
+ }
128
+
129
+ function flatten(adapters, field) {
130
+ return adapters.flatMap((adapter) =>
131
+ (adapter[field] ?? []).map((address) => ({
132
+ adapter: adapter.name,
133
+ address,
134
+ })),
135
+ );
136
+ }
137
+
138
+ export function createIpconfig({ platform = process.platform, exec } = {}) {
139
+ const run = createRunner({ platform, ...(exec ? { exec } : {}) });
140
+ async function execute(command, options = {}) {
141
+ if (typeof command !== "string" || !Object.hasOwn(COMMANDS, command))
142
+ throw new TypeError(`Unsupported ipconfig command: ${String(command)}`);
143
+ const { args, execution } = commandOptions(options, command);
144
+ return run(args, execution);
145
+ }
146
+ async function getRawOutput(options = {}) {
147
+ const { args, execution } = queryOptions(options);
148
+ return run(args, { ...execution, rawResult: options.rawResult ?? false });
149
+ }
150
+ async function getConfig(options = {}) {
151
+ validateOptions(options, CONFIG_OPTIONS);
152
+ const { format = "json", ...query } = options;
153
+ if (!["json", "xml", "yaml", "csv", "ndjson"].includes(format))
154
+ throw new TypeError(
155
+ "format must be one of: json, xml, yaml, csv, ndjson",
156
+ );
157
+ const { args, execution } = queryOptions(query);
158
+ const output = await run(args, execution);
159
+ const config = parseIpconfig(output);
160
+ config.hostname ??= systemHostname();
161
+ return formatConfig(config, format);
162
+ }
163
+ async function getAdapters(options) {
164
+ validateOptions(options, QUERY_OPTIONS);
165
+ return (await getConfig(options)).adapters;
166
+ }
167
+ async function getAdapter(nameOrIndex, options) {
168
+ if (!(typeof nameOrIndex === "string" || Number.isInteger(nameOrIndex)))
169
+ throw new TypeError(
170
+ "adapter selector must be a name or zero-based integer index",
171
+ );
172
+ const adapters = await getAdapters(options);
173
+ const found =
174
+ typeof nameOrIndex === "number"
175
+ ? adapters[nameOrIndex]
176
+ : adapters.find(
177
+ ({ name }) =>
178
+ name.toLocaleLowerCase() ===
179
+ nameOrIndex.trim().toLocaleLowerCase(),
180
+ );
181
+ if (!found)
182
+ throw new IpconfigError(`Network adapter not found: ${nameOrIndex}`, {
183
+ code: "ENOADAPTER",
184
+ });
185
+ return found;
186
+ }
187
+ async function addresses(family) {
188
+ return (await getAdapters({ all: true })).flatMap(
189
+ (adapter) => adapter[family],
190
+ );
191
+ }
192
+ async function release(options = {}) {
193
+ validateOptions(
194
+ options,
195
+ new Set([...EXECUTION_OPTIONS, "adapter", "ipv6"]),
196
+ );
197
+ if (options.ipv6 !== undefined && typeof options.ipv6 !== "boolean")
198
+ throw new TypeError("ipv6 must be a boolean");
199
+ const { ipv6 = false, ...rest } = options;
200
+ return execute(ipv6 ? "release6" : "release", rest);
201
+ }
202
+ async function renew(options = {}) {
203
+ validateOptions(
204
+ options,
205
+ new Set([...EXECUTION_OPTIONS, "adapter", "ipv6"]),
206
+ );
207
+ if (options.ipv6 !== undefined && typeof options.ipv6 !== "boolean")
208
+ throw new TypeError("ipv6 must be a boolean");
209
+ const { ipv6 = false, ...rest } = options;
210
+ return execute(ipv6 ? "renew6" : "renew", rest);
211
+ }
212
+ return {
213
+ execute,
214
+ getRawOutput,
215
+ getConfig,
216
+ getAdapters,
217
+ getAdapter,
218
+ getIPv4: async () => addresses("ipv4"),
219
+ getIPv6: async () => addresses("ipv6"),
220
+ getDefaultGateway: async () =>
221
+ flatten(await getAdapters({ all: true }), "defaultGateways"),
222
+ getDnsServers: async () =>
223
+ flatten(await getAdapters({ all: true }), "dnsServers"),
224
+ getMacAddresses: async () =>
225
+ (await getAdapters({ all: true })).flatMap(({ name, macAddress }) =>
226
+ macAddress ? [{ adapter: name, address: macAddress }] : [],
227
+ ),
228
+ getHostname: async () => systemHostname(),
229
+ flushDns: (options = {}) => execute("flushdns", options),
230
+ displayDns: (options = {}) => execute("displaydns", options),
231
+ registerDns: (options = {}) => execute("registerdns", options),
232
+ release,
233
+ renew,
234
+ showClassId: (options = {}) => execute("showclassid", options),
235
+ setClassId: (options = {}) => execute("setclassid", options),
236
+ showClassId6: (options = {}) => execute("showclassid6", options),
237
+ setClassId6: (options = {}) => execute("setclassid6", options),
238
+ getDnsCache: (options = {}) => execute("displaydns", options),
239
+ };
240
+ }
241
+
242
+ export const ipconfig = createIpconfig();
243
+ export { parseIpconfig, IpconfigError, createRunner };
244
+ export default ipconfig;
package/src/parser.cjs CHANGED
@@ -1,3 +1,3 @@
1
- "use strict";
2
- exports.parseIpconfig = (...args) =>
3
- import("./parser.js").then(({ parseIpconfig }) => parseIpconfig(...args));
1
+ "use strict";
2
+ exports.parseIpconfig = (...args) =>
3
+ import("./parser.js").then(({ parseIpconfig }) => parseIpconfig(...args));
package/src/parser.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- declare const parser: {
2
- parseIpconfig(
3
- output: string,
4
- ): Promise<{ hostname?: string; adapters: unknown[] }>;
5
- };
6
- export = parser;
1
+ declare const parser: {
2
+ parseIpconfig(
3
+ output: string,
4
+ ): Promise<{ hostname?: string; adapters: unknown[] }>;
5
+ };
6
+ export = parser;
package/src/parser.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import type { Config } from "./index.js";
2
- export function parseIpconfig(output: string): Config;
1
+ import type { Config } from "./index.js";
2
+ export function parseIpconfig(output: string): Config;