@nullix/zod-mongoose-studio 3.3.0 → 3.3.2

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 (119) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_fonts/57NSSoFy1VLVs2gqly8Ls9awBnZMFyXGrefpmqvdqmc-zJfbBtpgM4cDmcXBsqZNW79_kFnlpPd62b48glgdydA.woff2 +0 -0
  3. package/.output/public/_fonts/8VR2wSMN-3U4NbWAVYXlkRV6hA0jFBXP-0RtL3X7fko-x2gYI4qfmkRdxyQQUPaBZdZdgl1TeVrquF_TxHeM4lM.woff2 +0 -0
  4. package/.output/public/_fonts/GsKUclqeNLJ96g5AU593ug6yanivOiwjW_7zESNPChw-jHA4tBeM1bjF7LATGUpfBuSTyomIFrWBTzjF7txVYfg.woff2 +0 -0
  5. package/.output/public/_fonts/Ld1FnTo3yTIwDyGfTQ5-Fws9AWsCbKfMvgxduXr7JcY-W25bL8NF1fjpLRSOgJb7RoZPHqGQNwMTM7S9tHVoxx8.woff2 +0 -0
  6. package/.output/public/_fonts/NdzqRASp2bovDUhQT1IRE_EMqKJ2KYQdTCfFcBvL8yw-KhwZiS86o3fErOe5GGMExHUemmI_dBfaEFxjISZrBd0.woff2 +0 -0
  7. package/.output/public/_fonts/iTkrULNFJJkTvihIg1Vqi5IODRH_9btXCioVF5l98I8-AndUyau2HR2felA_ra8V2mutQgschhasE5FD1dXGJX8.woff2 +0 -0
  8. package/.output/public/_nuxt/B6lqezyA.js +27 -0
  9. package/.output/public/_nuxt/{IxYCG8hB.js → B7ScFmDB.js} +1 -1
  10. package/.output/public/_nuxt/B8gqXD2U.js +1 -0
  11. package/.output/public/_nuxt/{ikxafTHA.js → BC9tFNFa.js} +180 -184
  12. package/.output/public/_nuxt/{qwxNOyg7.js → Bbi9UBvi.js} +1 -1
  13. package/.output/public/_nuxt/{CNZorjDf.js → BcYnCaqL.js} +1 -1
  14. package/.output/public/_nuxt/{BzhSDy30.js → BlZ0x31M.js} +1 -1
  15. package/.output/public/_nuxt/Bw82IGM8.js +1 -0
  16. package/.output/public/_nuxt/{qQyvCkbV.js → C8xmwXC2.js} +1 -1
  17. package/.output/public/_nuxt/{Ce-T_1aD.js → C919AVYR.js} +1 -1
  18. package/.output/public/_nuxt/{nDZOJ7rN.js → CsQL5F-l.js} +2 -2
  19. package/.output/public/_nuxt/Czp4_2o-.js +33 -0
  20. package/.output/public/_nuxt/{CC3n3Aga.js → DJa7_7Hz.js} +1 -1
  21. package/.output/public/_nuxt/DKK0Mrrg.js +1 -0
  22. package/.output/public/_nuxt/DbUZZZZu.js +6 -0
  23. package/.output/public/_nuxt/{B7P0wQ35.js → DbqvVYbv.js} +1 -1
  24. package/.output/public/_nuxt/{Ick_23I4.js → JkiDMdYG.js} +1 -1
  25. package/.output/public/_nuxt/{81vqh61j.js → OVQ3C2Ge.js} +1 -1
  26. package/.output/public/_nuxt/{Cs8GXJCB.js → PXBhK7S_.js} +1 -1
  27. package/.output/public/_nuxt/{CAd-qHfI.js → YC-RLggm.js} +1 -1
  28. package/.output/public/_nuxt/builds/latest.json +1 -1
  29. package/.output/public/_nuxt/builds/meta/46174b5e-cab6-4a73-95f8-66a1c9b1be2b.json +1 -0
  30. package/.output/public/_nuxt/{tzj4nvDE.js → d1NBz903.js} +2 -2
  31. package/.output/public/_nuxt/{entry.J_CbC0oY.css → entry.DaqrxXuH.css} +1 -1
  32. package/.output/public/_nuxt/error-404.CTSXlFYa.css +1 -0
  33. package/.output/public/_nuxt/error-500.DqEfQnVZ.css +1 -0
  34. package/.output/public/_nuxt/{Bd32GOUy.js → iGzUozbF.js} +1 -1
  35. package/.output/public/_nuxt/{DjPT-NCE.js → iKW_mt0p.js} +1 -1
  36. package/.output/server/chunks/_/error-500.mjs.map +1 -1
  37. package/.output/server/chunks/build/client.precomputed.mjs +1 -1
  38. package/.output/server/chunks/build/{error-404-BN-7rNE4.mjs → error-404-KRkA0DPI.mjs} +10 -4
  39. package/.output/server/chunks/build/error-404-KRkA0DPI.mjs.map +1 -0
  40. package/.output/server/chunks/build/error-404-styles.I0NYw-Bn.mjs +8 -0
  41. package/.output/server/chunks/build/error-404-styles.I0NYw-Bn.mjs.map +1 -0
  42. package/.output/server/chunks/build/{error-500-DhIQ-2yt.mjs → error-500-DIIFf55p.mjs} +10 -4
  43. package/.output/server/chunks/build/error-500-DIIFf55p.mjs.map +1 -0
  44. package/.output/server/chunks/build/error-500-styles.D1cs9hQy.mjs +8 -0
  45. package/.output/server/chunks/build/error-500-styles.D1cs9hQy.mjs.map +1 -0
  46. package/.output/server/chunks/build/index-CB1N4gax.mjs +557 -0
  47. package/.output/server/chunks/build/index-CB1N4gax.mjs.map +1 -0
  48. package/.output/server/chunks/build/server.mjs +326 -248
  49. package/.output/server/chunks/build/server.mjs.map +1 -1
  50. package/.output/server/chunks/build/styles.mjs +4 -4
  51. package/.output/server/chunks/nitro/nitro.mjs +2319 -852
  52. package/.output/server/chunks/nitro/nitro.mjs.map +1 -1
  53. package/.output/server/chunks/routes/api/editor-types.get.mjs +113 -0
  54. package/.output/server/chunks/routes/api/editor-types.get.mjs.map +1 -0
  55. package/.output/server/chunks/routes/api/parse.post.mjs +26 -1448
  56. package/.output/server/chunks/routes/api/parse.post.mjs.map +1 -1
  57. package/.output/server/chunks/routes/api/resolve.get.mjs +6 -0
  58. package/.output/server/chunks/routes/api/resolve.get.mjs.map +1 -1
  59. package/.output/server/chunks/routes/api/studio/run.post.mjs +40 -0
  60. package/.output/server/chunks/routes/api/studio/run.post.mjs.map +1 -0
  61. package/.output/server/chunks/routes/renderer.mjs +1 -1
  62. package/.output/server/chunks/routes/renderer.mjs.map +1 -1
  63. package/.output/server/index.mjs +7 -1
  64. package/.output/server/index.mjs.map +1 -1
  65. package/.output/server/node_modules/acorn/dist/acorn.mjs +6266 -0
  66. package/.output/server/node_modules/acorn/package.json +50 -0
  67. package/.output/server/node_modules/ip-address/dist/common.js +112 -4
  68. package/.output/server/node_modules/ip-address/dist/ipv4.js +322 -87
  69. package/.output/server/node_modules/ip-address/dist/ipv6.js +654 -199
  70. package/.output/server/node_modules/ip-address/dist/v4/constants.js +45 -2
  71. package/.output/server/node_modules/ip-address/dist/v6/constants.js +55 -3
  72. package/.output/server/node_modules/ip-address/dist/v6/helpers.js +12 -3
  73. package/.output/server/node_modules/ip-address/package.json +49 -37
  74. package/.output/server/node_modules/ufo/dist/index.mjs +2 -2
  75. package/.output/server/node_modules/ufo/package.json +9 -9
  76. package/.output/server/package.json +4 -3
  77. package/README.md +65 -20
  78. package/app/assets/css/main.css +1 -0
  79. package/app/assets/css/studio.css +131 -0
  80. package/app/components/StudioConversion.vue +41 -0
  81. package/app/components/StudioDataPanel.vue +21 -0
  82. package/app/components/StudioEditor.vue +78 -102
  83. package/app/components/StudioExpandButton.vue +11 -0
  84. package/app/components/StudioOutput.vue +7 -1
  85. package/app/components/StudioSourcePanel.vue +16 -0
  86. package/app/components/StudioValidation.vue +47 -0
  87. package/app/components/ZodMongooseStudio.vue +216 -198
  88. package/app/composables/useStudioRunner.ts +142 -0
  89. package/app/pages/index.vue +1 -1
  90. package/app/plugins/monaco.client.ts +2 -0
  91. package/bin/index.mjs +5 -0
  92. package/nuxt.config.ts +4 -3
  93. package/package.json +6 -3
  94. package/server/api/editor-types.get.ts +7 -0
  95. package/server/api/parse.post.ts +17 -175
  96. package/server/api/studio/run.post.ts +29 -0
  97. package/server/middleware/security.ts +29 -7
  98. package/server/utils/editor-types.json +1 -0
  99. package/server/utils/example-data.ts +80 -0
  100. package/server/utils/parse-request.ts +28 -0
  101. package/server/utils/runner-client.ts +40 -0
  102. package/server/utils/studio-execution.ts +176 -0
  103. package/.output/public/_nuxt/BD40YYqZ.js +0 -33
  104. package/.output/public/_nuxt/BlQmLjV4.js +0 -1
  105. package/.output/public/_nuxt/CJ4Cv9fY.js +0 -61
  106. package/.output/public/_nuxt/builds/meta/495a8ebb-444d-4414-9b98-c181aa84d817.json +0 -1
  107. package/.output/public/_nuxt/error-404.64pZEiDn.css +0 -1
  108. package/.output/public/_nuxt/error-500.DHOpI0Ir.css +0 -1
  109. package/.output/public/_nuxt/rH_O4qXT.js +0 -1
  110. package/.output/server/chunks/build/error-404-BN-7rNE4.mjs.map +0 -1
  111. package/.output/server/chunks/build/error-404-styles.Byx46oLR.mjs +0 -8
  112. package/.output/server/chunks/build/error-404-styles.Byx46oLR.mjs.map +0 -1
  113. package/.output/server/chunks/build/error-500-DhIQ-2yt.mjs.map +0 -1
  114. package/.output/server/chunks/build/error-500-styles.BL3UT0Wa.mjs +0 -8
  115. package/.output/server/chunks/build/error-500-styles.BL3UT0Wa.mjs.map +0 -1
  116. package/.output/server/chunks/build/index-B4T6zHp0.mjs +0 -1904
  117. package/.output/server/chunks/build/index-B4T6zHp0.mjs.map +0 -1
  118. package/.output/server/node_modules/zod/index.js +0 -4
  119. /package/.output/public/_nuxt/{editor.CdBKkuhR.css → monaco.CdBKkuhR.css} +0 -0
@@ -28,13 +28,14 @@ exports.Address4 = void 0;
28
28
  const common = __importStar(require("./common"));
29
29
  const constants = __importStar(require("./v4/constants"));
30
30
  const address_error_1 = require("./address-error");
31
+ const isCorrect4 = common.isCorrect(constants.BITS);
31
32
  /**
32
33
  * Represents an IPv4 address
33
- * @class Address4
34
34
  * @param {string} address - An IPv4 address string
35
35
  */
36
36
  class Address4 {
37
37
  constructor(address) {
38
+ this.addressMinusSuffix = '';
38
39
  this.groups = constants.GROUPS;
39
40
  this.parsedAddress = [];
40
41
  this.parsedSubnet = '';
@@ -43,18 +44,27 @@ class Address4 {
43
44
  this.v4 = true;
44
45
  /**
45
46
  * Returns true if the address is correct, false otherwise
46
- * @memberof Address4
47
- * @instance
48
47
  * @returns {Boolean}
49
48
  */
50
- this.isCorrect = common.isCorrect(constants.BITS);
49
+ this.isCorrect = isCorrect4;
51
50
  /**
52
- * Returns true if the given address is in the subnet of the current address
53
- * @memberof Address4
54
- * @instance
51
+ * Returns true if the given address is in the subnet of the current address.
52
+ * An `Address6` is never in the subnet of an `Address4`; convert with
53
+ * `to4()` or `Address6.fromAddress4()` to compare across families.
55
54
  * @returns {boolean}
56
55
  */
57
56
  this.isInSubnet = common.isInSubnet;
57
+ /**
58
+ * Returns true if this address's host bits fall inside the given subnet,
59
+ * ignoring this address's own subnet mask. Prefer this over `isInSubnet`
60
+ * when classifying a single address, so the answer doesn't change with the
61
+ * CIDR suffix the caller happened to write — notably when the address came
62
+ * from untrusted input and the result backs a trust-boundary decision.
63
+ * An `Address6` is never in the subnet of an `Address4`; convert with
64
+ * `to4()` or `Address6.fromAddress4()` to compare across families.
65
+ * @returns {boolean}
66
+ */
67
+ this.isHostInSubnet = common.isHostInSubnet;
58
68
  this.address = address;
59
69
  const subnet = constants.RE_SUBNET_STRING.exec(address);
60
70
  if (subnet) {
@@ -66,94 +76,182 @@ class Address4 {
66
76
  }
67
77
  address = address.replace(constants.RE_SUBNET_STRING, '');
68
78
  }
79
+ // Four three-digit octets and three dots: the longest well-formed address
80
+ // is 15 characters. Longer input is rejected before parsing, as Address6
81
+ // does at its own limit.
82
+ const longest = constants.GROUPS * 4 - 1;
83
+ if (address.length > longest) {
84
+ throw new address_error_1.AddressError(`IPv4 addresses are at most ${longest} characters.`);
85
+ }
69
86
  this.addressMinusSuffix = address;
70
87
  this.parsedAddress = this.parse(address);
71
88
  }
89
+ /**
90
+ * Returns true if the given string is a valid IPv4 address (with optional
91
+ * CIDR subnet), false otherwise. Host bits in the subnet portion are
92
+ * allowed (e.g. `192.168.1.5/24` is valid); for strict network-address
93
+ * validation compare `correctForm()` to `startAddress().correctForm()`,
94
+ * or use `networkForm()`.
95
+ */
72
96
  static isValid(address) {
73
97
  try {
74
98
  // eslint-disable-next-line no-new
75
99
  new Address4(address);
76
100
  return true;
77
101
  }
78
- catch (e) {
102
+ catch {
79
103
  return false;
80
104
  }
81
105
  }
82
- /*
83
- * Parses a v4 address
106
+ /**
107
+ * Parses an IPv4 address string into its four octet groups and stores the
108
+ * result on `this.parsedAddress`. Called automatically by the constructor;
109
+ * you typically don't need to call it directly. Throws `AddressError` if
110
+ * the input is not a valid IPv4 address.
84
111
  */
85
112
  parse(address) {
86
113
  const groups = address.split('.');
114
+ // Checked before the general match so the error names the actual problem.
115
+ // Address6 rejects the same notation on its v4-in-v6 path.
116
+ if (groups.some((group) => /^0\d/.test(group))) {
117
+ throw new address_error_1.AddressError("IPv4 addresses can't have leading zeroes.");
118
+ }
87
119
  if (!address.match(constants.RE_ADDRESS)) {
88
120
  throw new address_error_1.AddressError('Invalid IPv4 address.');
89
121
  }
90
122
  return groups;
91
123
  }
92
124
  /**
93
- * Returns the correct form of an address
94
- * @memberof Address4
95
- * @instance
96
- * @returns {String}
125
+ * Returns the address in correct form: octets joined with `.` and any
126
+ * leading zeros stripped (e.g. `192.168.1.1`). For IPv4 this matches the
127
+ * canonical dotted-decimal representation.
97
128
  */
98
129
  correctForm() {
99
130
  return this.parsedAddress.map((part) => parseInt(part, 10)).join('.');
100
131
  }
101
132
  /**
102
- * Converts a hex string to an IPv4 address object
103
- * @memberof Address4
104
- * @static
133
+ * Construct an `Address4` from an address and a dotted-decimal subnet
134
+ * mask given as separate strings (e.g. as returned by Node's
135
+ * `os.networkInterfaces()`). Throws `AddressError` if the mask is
136
+ * non-contiguous (e.g. `255.0.255.0`).
137
+ * @example
138
+ * var address = Address4.fromAddressAndMask('192.168.1.1', '255.255.255.0');
139
+ * address.subnetMask; // 24
140
+ */
141
+ static fromAddressAndMask(address, mask) {
142
+ const bits = common.prefixLengthFromMask(new Address4(mask).bigInt(), constants.BITS);
143
+ return new Address4(`${address}/${bits}`);
144
+ }
145
+ /**
146
+ * Construct an `Address4` from an address and a Cisco-style wildcard mask
147
+ * given as separate strings (e.g. `0.0.0.255` for a `/24`). The wildcard
148
+ * mask is the bitwise inverse of the subnet mask. Throws `AddressError`
149
+ * if the mask is non-contiguous (e.g. `0.255.0.255`).
150
+ * @example
151
+ * var address = Address4.fromAddressAndWildcardMask('10.0.0.1', '0.0.0.255');
152
+ * address.subnetMask; // 24
153
+ */
154
+ static fromAddressAndWildcardMask(address, wildcardMask) {
155
+ const wildcard = new Address4(wildcardMask).bigInt();
156
+ const allOnes = (BigInt(1) << BigInt(constants.BITS)) - BigInt(1);
157
+ const mask = wildcard ^ allOnes;
158
+ const bits = common.prefixLengthFromMask(mask, constants.BITS);
159
+ return new Address4(`${address}/${bits}`);
160
+ }
161
+ /**
162
+ * Construct an `Address4` from a wildcard pattern with trailing `*`
163
+ * octets. The number of trailing wildcards determines the prefix
164
+ * length: each `*` represents 8 bits.
165
+ *
166
+ * Only trailing whole-octet wildcards are supported. Partial-octet
167
+ * wildcards (e.g. `192.168.0.1*`) and interior wildcards (e.g.
168
+ * `192.*.0.1`) throw `AddressError`.
169
+ * @example
170
+ * Address4.fromWildcard('192.168.0.*').subnet; // '/24'
171
+ * Address4.fromWildcard('192.168.*.*').subnet; // '/16'
172
+ * Address4.fromWildcard('*.*.*.*').subnet; // '/0'
173
+ */
174
+ static fromWildcard(input) {
175
+ const groups = input.split('.');
176
+ if (groups.length !== constants.GROUPS) {
177
+ throw new address_error_1.AddressError('Wildcard pattern must have 4 octets');
178
+ }
179
+ let firstWildcard = -1;
180
+ for (let i = 0; i < groups.length; i++) {
181
+ if (groups[i] === '*') {
182
+ if (firstWildcard === -1) {
183
+ firstWildcard = i;
184
+ }
185
+ }
186
+ else if (firstWildcard !== -1) {
187
+ throw new address_error_1.AddressError('Wildcard `*` must only appear in trailing octets (e.g. `192.168.0.*`)');
188
+ }
189
+ }
190
+ const trailing = firstWildcard === -1 ? 0 : groups.length - firstWildcard;
191
+ const replaced = groups.map((g) => (g === '*' ? '0' : g));
192
+ const subnetBits = constants.BITS - trailing * 8;
193
+ return new Address4(`${replaced.join('.')}/${subnetBits}`);
194
+ }
195
+ /**
196
+ * Converts a hex string to an IPv4 address object. Accepts 8 hex digits
197
+ * with optional `:` separators (e.g. `'7f000001'` or `'7f:00:00:01'`).
198
+ * Throws `AddressError` for any other length or for non-hex characters.
105
199
  * @param {string} hex - a hex string to convert
106
200
  * @returns {Address4}
107
201
  */
108
202
  static fromHex(hex) {
109
- const padded = hex.replace(/:/g, '').padStart(8, '0');
203
+ const stripped = hex.replace(/:/g, '');
204
+ if (!/^[0-9a-fA-F]{8}$/.test(stripped)) {
205
+ throw new address_error_1.AddressError('IPv4 hex must be exactly 8 hex digits');
206
+ }
110
207
  const groups = [];
111
- let i;
112
- for (i = 0; i < 8; i += 2) {
113
- const h = padded.slice(i, i + 2);
114
- groups.push(parseInt(h, 16));
208
+ for (let i = 0; i < 8; i += 2) {
209
+ groups.push(parseInt(stripped.slice(i, i + 2), 16));
115
210
  }
116
211
  return new Address4(groups.join('.'));
117
212
  }
118
213
  /**
119
- * Converts an integer into a IPv4 address object
120
- * @memberof Address4
121
- * @static
214
+ * Converts an integer into a IPv4 address object. The integer must be a
215
+ * non-negative safe integer in the range `[0, 2**32 - 1]`; otherwise
216
+ * `AddressError` is thrown.
122
217
  * @param {integer} integer - a number to convert
123
218
  * @returns {Address4}
124
219
  */
125
220
  static fromInteger(integer) {
126
- return Address4.fromHex(integer.toString(16));
221
+ if (!Number.isInteger(integer) || integer < 0 || integer > 0xffffffff) {
222
+ throw new address_error_1.AddressError('IPv4 integer must be in the range 0 to 2**32 - 1');
223
+ }
224
+ return Address4.fromHex(integer.toString(16).padStart(8, '0'));
127
225
  }
128
226
  /**
129
- * Return an address from in-addr.arpa form
130
- * @memberof Address4
131
- * @static
227
+ * Return an address from in-addr.arpa form: the four octets reversed, with
228
+ * or without the `.in-addr.arpa` suffix and root dot, in any case. Throws
229
+ * `AddressError` unless the reversed labels form a valid IPv4 address, so
230
+ * `fromArpa(x.reverseForm())` round-trips {@link reverseForm}.
132
231
  * @param {string} arpaFormAddress - an 'in-addr.arpa' form ipv4 address
133
232
  * @returns {Adress4}
134
233
  * @example
135
- * var address = Address4.fromArpa(42.2.0.192.in-addr.arpa.)
234
+ * var address = Address4.fromArpa('42.2.0.192.in-addr.arpa.')
136
235
  * address.correctForm(); // '192.0.2.42'
137
236
  */
138
237
  static fromArpa(arpaFormAddress) {
139
- // remove ending ".in-addr.arpa." or just "."
140
- const leader = arpaFormAddress.replace(/(\.in-addr\.arpa)?\.$/, '');
238
+ // remove an ending ".in-addr.arpa", in any case and with or without the
239
+ // root dot, as Address6.fromArpa does for ".ip6.arpa"
240
+ const leader = arpaFormAddress.replace(/(\.in-addr\.arpa)?\.?$/i, '');
141
241
  const address = leader.split('.').reverse().join('.');
142
242
  return new Address4(address);
143
243
  }
144
244
  /**
145
245
  * Converts an IPv4 address object to a hex string
146
- * @memberof Address4
147
- * @instance
148
246
  * @returns {String}
149
247
  */
150
248
  toHex() {
151
249
  return this.parsedAddress.map((part) => common.stringToPaddedHex(part)).join(':');
152
250
  }
153
251
  /**
154
- * Converts an IPv4 address object to an array of bytes
155
- * @memberof Address4
156
- * @instance
252
+ * Converts an IPv4 address object to an array of bytes.
253
+ *
254
+ * To get a Node.js `Buffer`, wrap the result: `Buffer.from(address.toArray())`.
157
255
  * @returns {Array}
158
256
  */
159
257
  toArray() {
@@ -161,8 +259,6 @@ class Address4 {
161
259
  }
162
260
  /**
163
261
  * Converts an IPv4 address object to an IPv6 address group
164
- * @memberof Address4
165
- * @instance
166
262
  * @returns {String}
167
263
  */
168
264
  toGroup6() {
@@ -175,8 +271,6 @@ class Address4 {
175
271
  }
176
272
  /**
177
273
  * Returns the address as a `bigint`
178
- * @memberof Address4
179
- * @instance
180
274
  * @returns {bigint}
181
275
  */
182
276
  bigInt() {
@@ -184,8 +278,6 @@ class Address4 {
184
278
  }
185
279
  /**
186
280
  * Helper function getting start address.
187
- * @memberof Address4
188
- * @instance
189
281
  * @returns {bigint}
190
282
  */
191
283
  _startAddress() {
@@ -194,8 +286,6 @@ class Address4 {
194
286
  /**
195
287
  * The first address in the range given by this address' subnet.
196
288
  * Often referred to as the Network Address.
197
- * @memberof Address4
198
- * @instance
199
289
  * @returns {Address4}
200
290
  */
201
291
  startAddress() {
@@ -204,18 +294,41 @@ class Address4 {
204
294
  /**
205
295
  * The first host address in the range given by this address's subnet ie
206
296
  * the first address after the Network Address
207
- * @memberof Address4
208
- * @instance
209
297
  * @returns {Address4}
210
298
  */
211
299
  startAddressExclusive() {
212
300
  const adjust = BigInt('1');
213
301
  return Address4.fromBigInt(this._startAddress() + adjust);
214
302
  }
303
+ /**
304
+ * Returns the address `n` addresses after this one (or before, when `n` is
305
+ * negative), keeping this address's subnet mask. Throws `AddressError` when
306
+ * the result would fall outside the IPv4 address space or `n` is not an
307
+ * integer.
308
+ * @param {number | bigint} n
309
+ * @returns {Address4}
310
+ * @example
311
+ * new Address4('10.0.0.0/24').offset(1).correctForm(); // '10.0.0.1'
312
+ */
313
+ offset(n) {
314
+ return Address4.fromBigInt(common.offsetBigInt(this.bigInt(), n, constants.BITS, 'IPv4')).withSubnetMask(this.subnetMask);
315
+ }
316
+ /**
317
+ * Returns the network that follows this address's network: the address after
318
+ * {@link endAddress}, with the same subnet mask. Throws `AddressError` when
319
+ * this network is the last one in the address space.
320
+ * @returns {Address4}
321
+ * @example
322
+ * new Address4('10.0.0.0/24').nextNetwork().networkForm(); // '10.0.1.0/24'
323
+ */
324
+ nextNetwork() {
325
+ return Address4.fromBigInt(common.offsetBigInt(this._endAddress(), 1, constants.BITS, 'IPv4')).withSubnetMask(this.subnetMask);
326
+ }
327
+ withSubnetMask(subnetMask) {
328
+ return new Address4(`${this.correctForm()}/${subnetMask}`);
329
+ }
215
330
  /**
216
331
  * Helper function getting end address.
217
- * @memberof Address4
218
- * @instance
219
332
  * @returns {bigint}
220
333
  */
221
334
  _endAddress() {
@@ -224,8 +337,6 @@ class Address4 {
224
337
  /**
225
338
  * The last address in the range given by this address' subnet
226
339
  * Often referred to as the Broadcast
227
- * @memberof Address4
228
- * @instance
229
340
  * @returns {Address4}
230
341
  */
231
342
  endAddress() {
@@ -234,8 +345,6 @@ class Address4 {
234
345
  /**
235
346
  * The last host address in the range given by this address's subnet ie
236
347
  * the last address prior to the Broadcast Address
237
- * @memberof Address4
238
- * @instance
239
348
  * @returns {Address4}
240
349
  */
241
350
  endAddressExclusive() {
@@ -243,38 +352,64 @@ class Address4 {
243
352
  return Address4.fromBigInt(this._endAddress() - adjust);
244
353
  }
245
354
  /**
246
- * Converts a BigInt to a v4 address object
247
- * @memberof Address4
248
- * @static
355
+ * The dotted-decimal form of the subnet mask, e.g. `255.255.240.0` for
356
+ * a `/20`. Returns an `Address4`; call `.correctForm()` for the string.
357
+ * @returns {Address4}
358
+ */
359
+ subnetMaskAddress() {
360
+ return Address4.fromBigInt(BigInt(`0b${'1'.repeat(this.subnetMask)}${'0'.repeat(constants.BITS - this.subnetMask)}`));
361
+ }
362
+ /**
363
+ * The Cisco-style wildcard mask, e.g. `0.0.0.255` for a `/24`. This is
364
+ * the bitwise inverse of `subnetMaskAddress()`. Returns an `Address4`;
365
+ * call `.correctForm()` for the string.
366
+ * @returns {Address4}
367
+ */
368
+ wildcardMask() {
369
+ return Address4.fromBigInt(BigInt(`0b${'0'.repeat(this.subnetMask)}${'1'.repeat(constants.BITS - this.subnetMask)}`));
370
+ }
371
+ /**
372
+ * The network address in CIDR string form, e.g. `192.168.1.0/24` for
373
+ * `192.168.1.5/24`. For an address with no explicit subnet the prefix is
374
+ * `/32`, e.g. `networkForm()` on `192.168.1.5` returns `192.168.1.5/32`.
375
+ * @returns {string}
376
+ */
377
+ networkForm() {
378
+ return `${this.startAddress().correctForm()}/${this.subnetMask}`;
379
+ }
380
+ /**
381
+ * Converts a BigInt to a v4 address object. The value must be in the
382
+ * range `[0, 2**32 - 1]`; otherwise `AddressError` is thrown.
249
383
  * @param {bigint} bigInt - a BigInt to convert
250
384
  * @returns {Address4}
251
385
  */
252
386
  static fromBigInt(bigInt) {
253
- return Address4.fromHex(bigInt.toString(16));
387
+ if (bigInt < BigInt(0) || bigInt > BigInt(0xffffffff)) {
388
+ throw new address_error_1.AddressError('IPv4 BigInt must be in the range 0 to 2**32 - 1');
389
+ }
390
+ return Address4.fromHex(bigInt.toString(16).padStart(8, '0'));
254
391
  }
255
392
  /**
256
- * Convert a byte array to an Address4 object
257
- * @memberof Address4
258
- * @static
393
+ * Convert a byte array to an Address4 object. Throws `AddressError` unless
394
+ * given exactly 4 integers from 0 to 255. Signed bytes are rejected, so
395
+ * this differs from `Address6.fromByteArray`, which folds them; the two
396
+ * contracts converge on this stricter form in the next major version.
397
+ *
398
+ * To convert from a Node.js `Buffer`, spread it: `Address4.fromByteArray([...buf])`.
259
399
  * @param {Array<number>} bytes - an array of 4 bytes (0-255)
260
400
  * @returns {Address4}
261
401
  */
262
402
  static fromByteArray(bytes) {
263
- if (bytes.length !== 4) {
264
- throw new address_error_1.AddressError('IPv4 addresses require exactly 4 bytes');
265
- }
266
- // Validate that all bytes are within valid range (0-255)
267
- for (let i = 0; i < bytes.length; i++) {
268
- if (!Number.isInteger(bytes[i]) || bytes[i] < 0 || bytes[i] > 255) {
269
- throw new address_error_1.AddressError('All bytes must be integers between 0 and 255');
270
- }
271
- }
403
+ common.assertByteArray(bytes, 4, 'IPv4', 0);
272
404
  return this.fromUnsignedByteArray(bytes);
273
405
  }
274
406
  /**
275
- * Convert an unsigned byte array to an Address4 object
276
- * @memberof Address4
277
- * @static
407
+ * Convert an unsigned byte array to an Address4 object. Throws
408
+ * `AddressError` unless given exactly 4 bytes, and rejects values outside
409
+ * 0 to 255 when parsing the resulting address.
410
+ *
411
+ * To convert from a Node.js `Buffer`, spread it:
412
+ * `Address4.fromUnsignedByteArray([...buf])`.
278
413
  * @param {Array<number>} bytes - an array of 4 unsigned bytes (0-255)
279
414
  * @returns {Address4}
280
415
  */
@@ -288,8 +423,6 @@ class Address4 {
288
423
  /**
289
424
  * Returns the first n bits of the address, defaulting to the
290
425
  * subnet mask
291
- * @memberof Address4
292
- * @instance
293
426
  * @returns {String}
294
427
  */
295
428
  mask(mask) {
@@ -300,19 +433,16 @@ class Address4 {
300
433
  }
301
434
  /**
302
435
  * Returns the bits in the given range as a base-2 string
303
- * @memberof Address4
304
- * @instance
305
436
  * @returns {string}
306
437
  */
307
438
  getBitsBase2(start, end) {
308
439
  return this.binaryZeroPad().slice(start, end);
309
440
  }
310
441
  /**
311
- * Return the reversed ip6.arpa form of the address
312
- * @memberof Address4
442
+ * Return the reversed in-addr.arpa form of the address, e.g.
443
+ * `42.2.0.192.in-addr.arpa.` for `192.0.2.42`.
313
444
  * @param {Object} options
314
445
  * @param {boolean} options.omitSuffix - omit the "in-addr.arpa" suffix
315
- * @instance
316
446
  * @returns {String}
317
447
  */
318
448
  reverseForm(options) {
@@ -327,29 +457,112 @@ class Address4 {
327
457
  }
328
458
  /**
329
459
  * Returns true if the given address is a multicast address
330
- * @memberof Address4
331
- * @instance
332
460
  * @returns {boolean}
333
461
  */
334
462
  isMulticast() {
335
- return this.isInSubnet(new Address4('224.0.0.0/4'));
463
+ return this.isHostInSubnet(MULTICAST_V4);
464
+ }
465
+ /**
466
+ * Returns true if the address is in one of the [RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918) private address ranges (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`).
467
+ * @returns {boolean}
468
+ */
469
+ isPrivate() {
470
+ return PRIVATE_V4.some((subnet) => this.isHostInSubnet(subnet));
471
+ }
472
+ /**
473
+ * Returns true if the address is in the loopback range `127.0.0.0/8` ([RFC 1122](https://datatracker.ietf.org/doc/html/rfc1122)).
474
+ * @returns {boolean}
475
+ */
476
+ isLoopback() {
477
+ return this.isHostInSubnet(LOOPBACK_V4);
478
+ }
479
+ /**
480
+ * Returns true if the address is in the link-local range `169.254.0.0/16` ([RFC 3927](https://datatracker.ietf.org/doc/html/rfc3927)).
481
+ * @returns {boolean}
482
+ */
483
+ isLinkLocal() {
484
+ return this.isHostInSubnet(LINK_LOCAL_V4);
485
+ }
486
+ /**
487
+ * Returns true if the address is the unspecified address `0.0.0.0`.
488
+ * @returns {boolean}
489
+ */
490
+ isUnspecified() {
491
+ return this.isHostInSubnet(UNSPECIFIED_V4);
492
+ }
493
+ /**
494
+ * Returns true if the address is the limited broadcast address `255.255.255.255` ([RFC 919](https://datatracker.ietf.org/doc/html/rfc919)).
495
+ * @returns {boolean}
496
+ */
497
+ isBroadcast() {
498
+ return this.isHostInSubnet(BROADCAST_V4);
499
+ }
500
+ /**
501
+ * Returns true if the address is in the carrier-grade NAT range `100.64.0.0/10` ([RFC 6598](https://datatracker.ietf.org/doc/html/rfc6598)).
502
+ * @returns {boolean}
503
+ */
504
+ isCGNAT() {
505
+ return this.isHostInSubnet(CGNAT_V4);
506
+ }
507
+ /**
508
+ * Returns true if the address is in one of the documentation ranges
509
+ * `192.0.2.0/24`, `198.51.100.0/24`, or `203.0.113.0/24` ([RFC 5737](https://datatracker.ietf.org/doc/html/rfc5737)).
510
+ * @returns {boolean}
511
+ */
512
+ isDocumentation() {
513
+ return DOCUMENTATION_V4.some((subnet) => this.isHostInSubnet(subnet));
514
+ }
515
+ /**
516
+ * Returns true if the address is in the benchmarking range `198.18.0.0/15` ([RFC 2544](https://datatracker.ietf.org/doc/html/rfc2544)).
517
+ * @returns {boolean}
518
+ */
519
+ isBenchmarking() {
520
+ return this.isHostInSubnet(BENCHMARKING_V4);
521
+ }
522
+ /**
523
+ * Returns true if the address is in the reserved range `240.0.0.0/4` ([RFC 1112](https://datatracker.ietf.org/doc/html/rfc1112)),
524
+ * which includes the limited broadcast address.
525
+ * @returns {boolean}
526
+ */
527
+ isReserved() {
528
+ return this.isHostInSubnet(RESERVED_V4);
529
+ }
530
+ /**
531
+ * Returns true if the address is globally reachable: not multicast, and not
532
+ * in any block the [IANA IPv4 Special-Purpose Address Registry](https://www.iana.org/assignments/iana-ipv4-special-registry/)
533
+ * marks as not globally reachable. That covers everything the individual
534
+ * classifiers name (private, loopback, link-local, CGNAT, unspecified,
535
+ * broadcast, documentation, benchmarking, reserved) and the blocks they do
536
+ * not, such as `0.0.0.0/8` and the IETF protocol assignments in
537
+ * `192.0.0.0/24`. This is the single predicate to use where a request must
538
+ * not reach an internal or special-purpose destination; see SECURITY.md.
539
+ * @returns {boolean}
540
+ */
541
+ isGlobal() {
542
+ return !this.isMulticast() && common.isGloballyReachable.call(this, SPECIAL_PURPOSE_V4);
336
543
  }
337
544
  /**
338
545
  * Returns a zero-padded base-2 string representation of the address
339
- * @memberof Address4
340
- * @instance
341
546
  * @returns {string}
342
547
  */
343
548
  binaryZeroPad() {
344
- return this.bigInt().toString(2).padStart(constants.BITS, '0');
549
+ if (this._binaryZeroPad === undefined) {
550
+ this._binaryZeroPad = this.bigInt().toString(2).padStart(constants.BITS, '0');
551
+ }
552
+ return this._binaryZeroPad;
345
553
  }
346
554
  /**
347
- * Groups an IPv4 address for inclusion at the end of an IPv6 address
555
+ * Groups an IPv4 address for inclusion at the end of an IPv6 address.
556
+ *
557
+ * Returns an HTML fragment: each half of the address is wrapped in a
558
+ * `<span>` carrying the group classes an address-inspector UI hovers on.
559
+ * The address content is HTML-escaped; anything you concatenate around it
560
+ * is your responsibility.
348
561
  * @returns {String}
349
562
  */
350
563
  groupForV6() {
351
564
  const segments = this.parsedAddress;
352
- return this.address.replace(constants.RE_ADDRESS, `<span class="hover-group group-v4 group-6">${segments
565
+ return this.correctForm().replace(constants.RE_ADDRESS, `<span class="hover-group group-v4 group-6">${segments
353
566
  .slice(0, 2)
354
567
  .join('.')}</span>.<span class="hover-group group-v4 group-7">${segments
355
568
  .slice(2, 4)
@@ -357,4 +570,26 @@ class Address4 {
357
570
  }
358
571
  }
359
572
  exports.Address4 = Address4;
573
+ const MULTICAST_V4 = new Address4('224.0.0.0/4');
574
+ const PRIVATE_V4 = [
575
+ new Address4('10.0.0.0/8'),
576
+ new Address4('172.16.0.0/12'),
577
+ new Address4('192.168.0.0/16'),
578
+ ];
579
+ const LOOPBACK_V4 = new Address4('127.0.0.0/8');
580
+ const LINK_LOCAL_V4 = new Address4('169.254.0.0/16');
581
+ const UNSPECIFIED_V4 = new Address4('0.0.0.0/32');
582
+ const BROADCAST_V4 = new Address4('255.255.255.255/32');
583
+ const CGNAT_V4 = new Address4('100.64.0.0/10');
584
+ const DOCUMENTATION_V4 = [
585
+ new Address4('192.0.2.0/24'),
586
+ new Address4('198.51.100.0/24'),
587
+ new Address4('203.0.113.0/24'),
588
+ ];
589
+ const BENCHMARKING_V4 = new Address4('198.18.0.0/15');
590
+ const RESERVED_V4 = new Address4('240.0.0.0/4');
591
+ const SPECIAL_PURPOSE_V4 = constants.SPECIAL_PURPOSE.map(([cidr, , reachable]) => ({
592
+ subnet: new Address4(cidr),
593
+ reachable,
594
+ }));
360
595
  //# sourceMappingURL=ipv4.js.map