@peac/net-node 0.12.6

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 (43) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +736 -0
  3. package/dist/codes.d.ts +47 -0
  4. package/dist/codes.d.ts.map +1 -0
  5. package/dist/codes.js +53 -0
  6. package/dist/codes.js.map +1 -0
  7. package/dist/constants.d.ts +39 -0
  8. package/dist/constants.d.ts.map +1 -0
  9. package/dist/constants.js +39 -0
  10. package/dist/constants.js.map +1 -0
  11. package/dist/evidence-utils.d.ts +49 -0
  12. package/dist/evidence-utils.d.ts.map +1 -0
  13. package/dist/evidence-utils.js +109 -0
  14. package/dist/evidence-utils.js.map +1 -0
  15. package/dist/impl.d.ts +331 -0
  16. package/dist/impl.d.ts.map +1 -0
  17. package/dist/impl.js +1188 -0
  18. package/dist/impl.js.map +1 -0
  19. package/dist/index.d.ts +772 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +1121 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/internal.d.ts +28 -0
  24. package/dist/internal.d.ts.map +1 -0
  25. package/dist/internal.js +59 -0
  26. package/dist/internal.js.map +1 -0
  27. package/dist/receipt-resolver.d.ts +65 -0
  28. package/dist/receipt-resolver.d.ts.map +1 -0
  29. package/dist/receipt-resolver.js +140 -0
  30. package/dist/receipt-resolver.js.map +1 -0
  31. package/dist/receipt-url-middleware.d.ts +87 -0
  32. package/dist/receipt-url-middleware.d.ts.map +1 -0
  33. package/dist/receipt-url-middleware.js +141 -0
  34. package/dist/receipt-url-middleware.js.map +1 -0
  35. package/dist/ssrf.d.ts +114 -0
  36. package/dist/ssrf.d.ts.map +1 -0
  37. package/dist/ssrf.js +214 -0
  38. package/dist/ssrf.js.map +1 -0
  39. package/dist/testing.d.ts +65 -0
  40. package/dist/testing.d.ts.map +1 -0
  41. package/dist/testing.js +103 -0
  42. package/dist/testing.js.map +1 -0
  43. package/package.json +79 -0
package/dist/ssrf.d.ts ADDED
@@ -0,0 +1,114 @@
1
+ /**
2
+ * SSRF Policy Types and Validation
3
+ *
4
+ * Local definitions for SSRF protection configuration.
5
+ * These types are specific to @peac/net-node's SSRF protection.
6
+ *
7
+ * @internal
8
+ * @module @peac/net-node/ssrf
9
+ */
10
+ /**
11
+ * Acknowledgment constant for allowing dangerous CIDR ranges
12
+ *
13
+ * When policy.allowCidrs contains private/dangerous ranges,
14
+ * this acknowledgment must be set to confirm intentional usage.
15
+ */
16
+ export declare const ALLOW_DANGEROUS_CIDRS_ACK: "I_UNDERSTAND_ALLOWING_PRIVATE_CIDRS_IS_DANGEROUS";
17
+ /**
18
+ * SSRF policy configuration
19
+ *
20
+ * Controls how SSRF protection handles URLs and IP addresses.
21
+ */
22
+ export interface SSRFPolicy {
23
+ /**
24
+ * Additional CIDR ranges to ALLOW (bypass blocking).
25
+ * Use with extreme caution - only for trusted internal services.
26
+ * Requires ack_allow_dangerous_cidrs if ranges overlap with private/dangerous IPs.
27
+ */
28
+ allowCidrs?: string[];
29
+ /**
30
+ * Additional CIDR ranges to BLOCK (in addition to default private ranges).
31
+ * Applied after allowCidrs.
32
+ */
33
+ blockedCidrs?: string[];
34
+ /**
35
+ * Acknowledgment for allowing dangerous CIDR ranges.
36
+ * Required when allowCidrs contains private/dangerous ranges.
37
+ * Must be exactly ALLOW_DANGEROUS_CIDRS_ACK to be valid.
38
+ */
39
+ ack_allow_dangerous_cidrs?: string;
40
+ /**
41
+ * Allow CGNAT (Carrier-Grade NAT) range: 100.64.0.0/10
42
+ * Use with caution - requires explicit acknowledgment.
43
+ */
44
+ allowCgnat?: boolean;
45
+ /**
46
+ * Acknowledgment for allowing CGNAT range.
47
+ * Required when allowCgnat is true.
48
+ */
49
+ ack_allow_cgnat?: string;
50
+ /**
51
+ * Require HTTPS (default true). Set to false to allow HTTP URLs.
52
+ * @default true
53
+ */
54
+ requireHttps?: boolean;
55
+ /**
56
+ * Allow IP literals in URLs (default false).
57
+ * When false, only hostnames are allowed (not direct IP addresses).
58
+ * @default false
59
+ */
60
+ allowIPLiterals?: boolean;
61
+ /**
62
+ * Allowed ports (default [80, 443]).
63
+ * Only these ports are allowed in URLs. Other ports are blocked.
64
+ */
65
+ allowPorts?: number[];
66
+ /**
67
+ * Allow credentials in URLs (default false).
68
+ * When false, URLs with username:password are rejected.
69
+ * @default false
70
+ */
71
+ allowCredentials?: boolean;
72
+ }
73
+ /**
74
+ * Default SSRF policy - blocks all private IP ranges
75
+ */
76
+ export declare const DEFAULT_SSRF_POLICY: SSRFPolicy;
77
+ /**
78
+ * Result of URL validation for SSRF
79
+ */
80
+ export type UrlValidationResult = {
81
+ ok: true;
82
+ value: URL;
83
+ } | {
84
+ ok: false;
85
+ code: string;
86
+ error: string;
87
+ };
88
+ /**
89
+ * Validate a URL string for SSRF protection (string-level checks only)
90
+ *
91
+ * This performs URL parsing and basic string-level validation.
92
+ * NOTE: This is NOT sufficient for full SSRF protection - DNS resolution
93
+ * and IP validation must also be performed at connection time.
94
+ *
95
+ * @param urlString - The URL to validate
96
+ * @param policy - SSRF policy (affects which checks are applied)
97
+ * @returns Validation result with parsed URL or error
98
+ */
99
+ export declare function validateUrlForSSRF(urlString: string, policy?: SSRFPolicy): UrlValidationResult;
100
+ /**
101
+ * Error codes for trust boundary violations
102
+ *
103
+ * Used for SSRF-related errors in safeFetch operations.
104
+ */
105
+ export declare const TRUST_ERROR_CODES: {
106
+ readonly SSRF_URL_REJECTED: "E_NET_SSRF_URL_REJECTED";
107
+ readonly SSRF_DNS_RESOLVED_PRIVATE: "E_NET_SSRF_DNS_RESOLVED_PRIVATE";
108
+ readonly SSRF_ALL_IPS_BLOCKED: "E_NET_SSRF_ALL_IPS_BLOCKED";
109
+ readonly SSRF_REDIRECT_BLOCKED: "E_NET_SSRF_REDIRECT_BLOCKED";
110
+ readonly SSRF_TOO_MANY_REDIRECTS: "E_NET_SSRF_TOO_MANY_REDIRECTS";
111
+ readonly SSRF_IPV6_ZONE_ID: "E_NET_SSRF_IPV6_ZONE_ID";
112
+ readonly SSRF_INVALID_HOST: "E_NET_SSRF_INVALID_HOST";
113
+ };
114
+ //# sourceMappingURL=ssrf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssrf.d.ts","sourceRoot":"","sources":["../src/ssrf.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,EACpC,kDAA2D,CAAC;AAE9D;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAExB;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAEnC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB,EAAE,UAGjC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAC3B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,GAAG,CAAA;CAAE,GACxB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAiE/C;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,mBAAmB,CAqH9F;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;CAQpB,CAAC"}
package/dist/ssrf.js ADDED
@@ -0,0 +1,214 @@
1
+ /**
2
+ * SSRF Policy Types and Validation
3
+ *
4
+ * Local definitions for SSRF protection configuration.
5
+ * These types are specific to @peac/net-node's SSRF protection.
6
+ *
7
+ * @internal
8
+ * @module @peac/net-node/ssrf
9
+ */
10
+ /**
11
+ * Acknowledgment constant for allowing dangerous CIDR ranges
12
+ *
13
+ * When policy.allowCidrs contains private/dangerous ranges,
14
+ * this acknowledgment must be set to confirm intentional usage.
15
+ */
16
+ export const ALLOW_DANGEROUS_CIDRS_ACK = 'I_UNDERSTAND_ALLOWING_PRIVATE_CIDRS_IS_DANGEROUS';
17
+ /**
18
+ * Default SSRF policy - blocks all private IP ranges
19
+ */
20
+ export const DEFAULT_SSRF_POLICY = {
21
+ allowCidrs: [],
22
+ blockedCidrs: [],
23
+ };
24
+ /**
25
+ * RFC 1918 private IPv4 ranges and localhost
26
+ */
27
+ const PRIVATE_IPV4_PATTERNS = [
28
+ /^127\./, // Loopback
29
+ /^10\./, // Class A private
30
+ /^172\.(1[6-9]|2[0-9]|3[0-1])\./, // Class B private (172.16-31.x.x)
31
+ /^192\.168\./, // Class C private
32
+ /^0\./, // "This network"
33
+ /^100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\./, // CGNAT 100.64.0.0/10
34
+ /^169\.254\./, // Link-local
35
+ /^192\.0\.0\./, // IETF protocol assignments
36
+ /^192\.0\.2\./, // TEST-NET-1
37
+ /^198\.51\.100\./, // TEST-NET-2
38
+ /^203\.0\.113\./, // TEST-NET-3
39
+ /^192\.88\.99\./, // 6to4 relay anycast
40
+ /^198\.18\./, // Benchmark testing
41
+ /^224\./, // Multicast
42
+ /^240\./, // Reserved (future use)
43
+ /^255\.255\.255\.255$/, // Broadcast
44
+ ];
45
+ /**
46
+ * Check if a hostname looks like a private IPv4 address
47
+ */
48
+ function isPrivateIPv4Literal(hostname) {
49
+ // Check if it looks like an IPv4 address
50
+ if (!/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(hostname)) {
51
+ return false;
52
+ }
53
+ return PRIVATE_IPV4_PATTERNS.some((pattern) => pattern.test(hostname));
54
+ }
55
+ /**
56
+ * Check if a hostname is localhost or a loopback address
57
+ */
58
+ function isLoopback(hostname) {
59
+ const lower = hostname.toLowerCase();
60
+ return (lower === 'localhost' ||
61
+ lower === 'localhost.' ||
62
+ lower.endsWith('.localhost') ||
63
+ lower === '127.0.0.1' ||
64
+ lower === '[::1]' ||
65
+ lower === '::1');
66
+ }
67
+ /**
68
+ * Check if a hostname is an IP literal (IPv4 or IPv6)
69
+ */
70
+ function isIPLiteral(hostname) {
71
+ // IPv4 pattern
72
+ if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(hostname)) {
73
+ return true;
74
+ }
75
+ // IPv6 pattern (bracketed or not)
76
+ if (hostname.startsWith('[') || hostname.includes(':')) {
77
+ return true;
78
+ }
79
+ return false;
80
+ }
81
+ /**
82
+ * Validate a URL string for SSRF protection (string-level checks only)
83
+ *
84
+ * This performs URL parsing and basic string-level validation.
85
+ * NOTE: This is NOT sufficient for full SSRF protection - DNS resolution
86
+ * and IP validation must also be performed at connection time.
87
+ *
88
+ * @param urlString - The URL to validate
89
+ * @param policy - SSRF policy (affects which checks are applied)
90
+ * @returns Validation result with parsed URL or error
91
+ */
92
+ export function validateUrlForSSRF(urlString, policy) {
93
+ // Step 1: Parse URL
94
+ let parsedUrl;
95
+ try {
96
+ parsedUrl = new URL(urlString);
97
+ }
98
+ catch {
99
+ return {
100
+ ok: false,
101
+ code: 'E_NET_SSRF_INVALID_URL',
102
+ error: 'Invalid URL format',
103
+ };
104
+ }
105
+ // Step 2: Protocol validation - only https allowed by default (http is risky)
106
+ if (parsedUrl.protocol !== 'https:' && parsedUrl.protocol !== 'http:') {
107
+ return {
108
+ ok: false,
109
+ code: 'E_NET_SSRF_INVALID_PROTOCOL',
110
+ error: `Protocol ${parsedUrl.protocol} not allowed`,
111
+ };
112
+ }
113
+ // Step 3: Block HTTP by default unless policy allows (requireHttps: false)
114
+ const requireHttps = policy?.requireHttps !== false; // default true
115
+ if (parsedUrl.protocol === 'http:' && requireHttps) {
116
+ return {
117
+ ok: false,
118
+ code: 'E_NET_SSRF_HTTP_BLOCKED',
119
+ error: 'HTTP protocol blocked - use HTTPS',
120
+ };
121
+ }
122
+ // Step 4: Basic hostname validation
123
+ const hostname = parsedUrl.hostname;
124
+ if (!hostname || hostname.length === 0) {
125
+ return {
126
+ ok: false,
127
+ code: 'E_NET_SSRF_INVALID_HOST',
128
+ error: 'Empty hostname',
129
+ };
130
+ }
131
+ // Step 5: Check for IPv6 zone IDs (security risk)
132
+ if (hostname.includes('%')) {
133
+ return {
134
+ ok: false,
135
+ code: 'E_NET_SSRF_IPV6_ZONE_ID',
136
+ error: 'IPv6 zone IDs not allowed',
137
+ };
138
+ }
139
+ // Step 6: Block localhost/loopback
140
+ if (isLoopback(hostname)) {
141
+ return {
142
+ ok: false,
143
+ code: 'E_NET_SSRF_LOCALHOST_BLOCKED',
144
+ error: 'Localhost/loopback addresses not allowed',
145
+ };
146
+ }
147
+ // Step 7: Block private IPv4 literals (unless policy allows)
148
+ if (isPrivateIPv4Literal(hostname)) {
149
+ // Check if policy explicitly allows this CIDR
150
+ const allowed = policy?.allowCidrs?.some((cidr) => {
151
+ // Simple check - if any allowCidr is set, we let DNS-level validation handle it
152
+ return cidr.length > 0;
153
+ });
154
+ if (!allowed) {
155
+ return {
156
+ ok: false,
157
+ code: 'E_NET_SSRF_PRIVATE_IP_BLOCKED',
158
+ error: 'Private IP addresses not allowed',
159
+ };
160
+ }
161
+ }
162
+ // Step 8: Block IP literals by default unless policy allows
163
+ const allowIPLiterals = policy?.allowIPLiterals === true;
164
+ if (isIPLiteral(hostname) && !allowIPLiterals) {
165
+ return {
166
+ ok: false,
167
+ code: 'E_NET_SSRF_IP_LITERAL_BLOCKED',
168
+ error: 'IP literals not allowed - use hostnames',
169
+ };
170
+ }
171
+ // Step 9: Block credentials in URLs by default
172
+ const allowCredentials = policy?.allowCredentials === true;
173
+ if (!allowCredentials && (parsedUrl.username || parsedUrl.password)) {
174
+ return {
175
+ ok: false,
176
+ code: 'E_NET_SSRF_CREDENTIALS_BLOCKED',
177
+ error: 'Credentials in URLs not allowed',
178
+ };
179
+ }
180
+ // Step 10: Port validation - only allow standard ports by default
181
+ // allowPorts is ADDITIVE to the default [80, 443]
182
+ const defaultPorts = [80, 443];
183
+ const additionalPorts = policy?.allowPorts ?? [];
184
+ const allowedPorts = [...new Set([...defaultPorts, ...additionalPorts])];
185
+ const portStr = parsedUrl.port;
186
+ const effectivePort = portStr
187
+ ? parseInt(portStr, 10)
188
+ : parsedUrl.protocol === 'https:'
189
+ ? 443
190
+ : 80;
191
+ if (!allowedPorts.includes(effectivePort)) {
192
+ return {
193
+ ok: false,
194
+ code: 'E_NET_SSRF_PORT_BLOCKED',
195
+ error: `Port ${effectivePort} not allowed`,
196
+ };
197
+ }
198
+ return { ok: true, value: parsedUrl };
199
+ }
200
+ /**
201
+ * Error codes for trust boundary violations
202
+ *
203
+ * Used for SSRF-related errors in safeFetch operations.
204
+ */
205
+ export const TRUST_ERROR_CODES = {
206
+ SSRF_URL_REJECTED: 'E_NET_SSRF_URL_REJECTED',
207
+ SSRF_DNS_RESOLVED_PRIVATE: 'E_NET_SSRF_DNS_RESOLVED_PRIVATE',
208
+ SSRF_ALL_IPS_BLOCKED: 'E_NET_SSRF_ALL_IPS_BLOCKED',
209
+ SSRF_REDIRECT_BLOCKED: 'E_NET_SSRF_REDIRECT_BLOCKED',
210
+ SSRF_TOO_MANY_REDIRECTS: 'E_NET_SSRF_TOO_MANY_REDIRECTS',
211
+ SSRF_IPV6_ZONE_ID: 'E_NET_SSRF_IPV6_ZONE_ID',
212
+ SSRF_INVALID_HOST: 'E_NET_SSRF_INVALID_HOST',
213
+ };
214
+ //# sourceMappingURL=ssrf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssrf.js","sourceRoot":"","sources":["../src/ssrf.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,yBAAyB,GACpC,kDAA2D,CAAC;AAmE9D;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAe;IAC7C,UAAU,EAAE,EAAE;IACd,YAAY,EAAE,EAAE;CACjB,CAAC;AASF;;GAEG;AACH,MAAM,qBAAqB,GAAG;IAC5B,QAAQ,EAAE,WAAW;IACrB,OAAO,EAAE,kBAAkB;IAC3B,gCAAgC,EAAE,kCAAkC;IACpE,aAAa,EAAE,kBAAkB;IACjC,MAAM,EAAE,iBAAiB;IACzB,gDAAgD,EAAE,sBAAsB;IACxE,aAAa,EAAE,aAAa;IAC5B,cAAc,EAAE,4BAA4B;IAC5C,cAAc,EAAE,aAAa;IAC7B,iBAAiB,EAAE,aAAa;IAChC,gBAAgB,EAAE,aAAa;IAC/B,gBAAgB,EAAE,qBAAqB;IACvC,YAAY,EAAE,oBAAoB;IAClC,QAAQ,EAAE,YAAY;IACtB,QAAQ,EAAE,wBAAwB;IAClC,sBAAsB,EAAE,YAAY;CACrC,CAAC;AAEF;;GAEG;AACH,SAAS,oBAAoB,CAAC,QAAgB;IAC5C,yCAAyC;IACzC,IAAI,CAAC,sCAAsC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,qBAAqB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;AACzE,CAAC;AAED;;GAEG;AACH,SAAS,UAAU,CAAC,QAAgB;IAClC,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACrC,OAAO,CACL,KAAK,KAAK,WAAW;QACrB,KAAK,KAAK,YAAY;QACtB,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC;QAC5B,KAAK,KAAK,WAAW;QACrB,KAAK,KAAK,OAAO;QACjB,KAAK,KAAK,KAAK,CAChB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,QAAgB;IACnC,eAAe;IACf,IAAI,sCAAsC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1D,OAAO,IAAI,CAAC;IACd,CAAC;IACD,kCAAkC;IAClC,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACvD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAAiB,EAAE,MAAmB;IACvE,oBAAoB;IACpB,IAAI,SAAc,CAAC;IACnB,IAAI,CAAC;QACH,SAAS,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,wBAAwB;YAC9B,KAAK,EAAE,oBAAoB;SAC5B,CAAC;IACJ,CAAC;IAED,8EAA8E;IAC9E,IAAI,SAAS,CAAC,QAAQ,KAAK,QAAQ,IAAI,SAAS,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACtE,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,6BAA6B;YACnC,KAAK,EAAE,YAAY,SAAS,CAAC,QAAQ,cAAc;SACpD,CAAC;IACJ,CAAC;IAED,2EAA2E;IAC3E,MAAM,YAAY,GAAG,MAAM,EAAE,YAAY,KAAK,KAAK,CAAC,CAAC,eAAe;IACpE,IAAI,SAAS,CAAC,QAAQ,KAAK,OAAO,IAAI,YAAY,EAAE,CAAC;QACnD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,yBAAyB;YAC/B,KAAK,EAAE,mCAAmC;SAC3C,CAAC;IACJ,CAAC;IAED,oCAAoC;IACpC,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAC;IACpC,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,yBAAyB;YAC/B,KAAK,EAAE,gBAAgB;SACxB,CAAC;IACJ,CAAC;IAED,kDAAkD;IAClD,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,yBAAyB;YAC/B,KAAK,EAAE,2BAA2B;SACnC,CAAC;IACJ,CAAC;IAED,mCAAmC;IACnC,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,8BAA8B;YACpC,KAAK,EAAE,0CAA0C;SAClD,CAAC;IACJ,CAAC;IAED,6DAA6D;IAC7D,IAAI,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnC,8CAA8C;QAC9C,MAAM,OAAO,GAAG,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;YAChD,gFAAgF;YAChF,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,+BAA+B;gBACrC,KAAK,EAAE,kCAAkC;aAC1C,CAAC;QACJ,CAAC;IACH,CAAC;IAED,4DAA4D;IAC5D,MAAM,eAAe,GAAG,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IACzD,IAAI,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;QAC9C,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,+BAA+B;YACrC,KAAK,EAAE,yCAAyC;SACjD,CAAC;IACJ,CAAC;IAED,+CAA+C;IAC/C,MAAM,gBAAgB,GAAG,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC3D,IAAI,CAAC,gBAAgB,IAAI,CAAC,SAAS,CAAC,QAAQ,IAAI,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpE,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,gCAAgC;YACtC,KAAK,EAAE,iCAAiC;SACzC,CAAC;IACJ,CAAC;IAED,kEAAkE;IAClE,kDAAkD;IAClD,MAAM,YAAY,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IAC/B,MAAM,eAAe,GAAG,MAAM,EAAE,UAAU,IAAI,EAAE,CAAC;IACjD,MAAM,YAAY,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,YAAY,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;IACzE,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC;IAC/B,MAAM,aAAa,GAAG,OAAO;QAC3B,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;QACvB,CAAC,CAAC,SAAS,CAAC,QAAQ,KAAK,QAAQ;YAC/B,CAAC,CAAC,GAAG;YACL,CAAC,CAAC,EAAE,CAAC;IAET,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QAC1C,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,yBAAyB;YAC/B,KAAK,EAAE,QAAQ,aAAa,cAAc;SAC3C,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AACxC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,iBAAiB,EAAE,yBAAyB;IAC5C,yBAAyB,EAAE,iCAAiC;IAC5D,oBAAoB,EAAE,4BAA4B;IAClD,qBAAqB,EAAE,6BAA6B;IACpD,uBAAuB,EAAE,+BAA+B;IACxD,iBAAiB,EAAE,yBAAyB;IAC5C,iBAAiB,EAAE,yBAAyB;CACpC,CAAC"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @peac/net-node Testing Utilities
3
+ *
4
+ * This module exports internal utilities for testing purposes only.
5
+ * These APIs are NOT part of the public contract and may change without notice.
6
+ *
7
+ * Import via:
8
+ * import { _internals } from '@peac/net-node/testing';
9
+ *
10
+ * DO NOT use these in production code.
11
+ *
12
+ * @module @peac/net-node/testing
13
+ */
14
+ import { isPrivateIP, resolveDnsSecure, createPinnedAgent, parseCidr, matchesAnyCidr, validateAllowCidrsAck, cidrOverlapsDangerousRanges, getRegistrableDomain, isRedirectAllowed, normalizeHostname, sanitizeUrl, readBodyWithLimit, attemptWithFallback, hasHeaderCaseInsensitive, stripHopByHopHeaders, hasIPv6ZoneId, normalizeIPv4MappedIPv6, canonicalizeHost, emitAuditEvent, createRequestAuditContext, hashIpAddress, redactIp, resetAuditQueueStats } from './impl.js';
15
+ import { jcsCanonicalizeValue } from './evidence-utils.js';
16
+ import { finalizeEvidence } from './internal.js';
17
+ export type { DnsResolver, HttpClient, HttpClientTimeouts, SafeFetchAuditHook, SafeFetchEvent, DnsAnswer, SafeFetchEvidence, SafeFetchEvidenceCore, EvidenceLevel, RedactedIpInfo, RequestAuditContext, } from './index.js';
18
+ export { canonicalizeEvidence, computeEvidenceDigest, getAuditQueueStats, MAX_PENDING_AUDIT_EVENTS, DANGEROUS_PORTS, ALLOW_DANGEROUS_PORTS_ACK, SAFE_FETCH_ERROR_CODES, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_REDIRECTS, DEFAULT_ALLOWED_METHODS, DEFAULT_MAX_RESPONSE_BYTES, MAX_JWKS_RESPONSE_BYTES, RESPONSE_BYTES_MEASUREMENT, DEFAULT_TIMEOUTS, ALLOW_MIXED_DNS_ACK, SAFE_FETCH_EVENT_SCHEMA_VERSION, SAFE_FETCH_EVIDENCE_SCHEMA_VERSION, } from './index.js';
19
+ /**
20
+ * Internal utilities for testing.
21
+ *
22
+ * These utilities provide access to internal implementation details
23
+ * for testing purposes only. They are NOT stable APIs.
24
+ *
25
+ * @example
26
+ * import { _internals } from '@peac/net-node/testing';
27
+ *
28
+ * // Access internal functions
29
+ * const isPrivate = _internals.isPrivateIP('10.0.0.1');
30
+ */
31
+ export declare const _internals: {
32
+ readonly isPrivateIP: typeof isPrivateIP;
33
+ readonly resolveDnsSecure: typeof resolveDnsSecure;
34
+ readonly createPinnedAgent: typeof createPinnedAgent;
35
+ readonly parseCidr: typeof parseCidr;
36
+ readonly matchesAnyCidr: typeof matchesAnyCidr;
37
+ readonly validateAllowCidrsAck: typeof validateAllowCidrsAck;
38
+ readonly cidrOverlapsDangerousRanges: typeof cidrOverlapsDangerousRanges;
39
+ readonly getRegistrableDomain: typeof getRegistrableDomain;
40
+ readonly isRedirectAllowed: typeof isRedirectAllowed;
41
+ readonly normalizeHostname: typeof normalizeHostname;
42
+ readonly sanitizeUrl: typeof sanitizeUrl;
43
+ readonly readBodyWithLimit: typeof readBodyWithLimit;
44
+ readonly attemptWithFallback: typeof attemptWithFallback;
45
+ readonly hasHeaderCaseInsensitive: typeof hasHeaderCaseInsensitive;
46
+ readonly stripHopByHopHeaders: typeof stripHopByHopHeaders;
47
+ readonly hasIPv6ZoneId: typeof hasIPv6ZoneId;
48
+ readonly normalizeIPv4MappedIPv6: typeof normalizeIPv4MappedIPv6;
49
+ readonly canonicalizeHost: typeof canonicalizeHost;
50
+ readonly emitAuditEvent: typeof emitAuditEvent;
51
+ readonly createRequestAuditContext: typeof createRequestAuditContext;
52
+ readonly hashIpAddress: typeof hashIpAddress;
53
+ readonly redactIp: typeof redactIp;
54
+ readonly jcsCanonicalizeValue: typeof jcsCanonicalizeValue;
55
+ readonly resetAuditQueueStats: typeof resetAuditQueueStats;
56
+ readonly finalizeEvidence: typeof finalizeEvidence;
57
+ readonly BLOCKED_IPV4_RANGES: Set<string>;
58
+ readonly BLOCKED_IPV6_RANGES: Set<string>;
59
+ readonly ADDITIONAL_BLOCKED_CIDRS_V4: [import("ipaddr.js").IPv4, number][];
60
+ readonly ADDITIONAL_BLOCKED_CIDRS_V6: [import("ipaddr.js").IPv6, number][];
61
+ readonly HOP_BY_HOP_HEADERS: Set<string>;
62
+ readonly SAFE_FETCH_EVENT_SCHEMA_VERSION: "peac-safe-fetch-event/0.1";
63
+ readonly SAFE_FETCH_EVIDENCE_SCHEMA_VERSION: "peac-safe-fetch-evidence/0.1";
64
+ };
65
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAEL,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EAEjB,SAAS,EACT,cAAc,EACd,qBAAqB,EACrB,2BAA2B,EAE3B,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EAEX,iBAAiB,EACjB,mBAAmB,EAEnB,wBAAwB,EACxB,oBAAoB,EAEpB,aAAa,EACb,uBAAuB,EACvB,gBAAgB,EAEhB,cAAc,EACd,yBAAyB,EAEzB,aAAa,EACb,QAAQ,EAER,oBAAoB,EAOrB,MAAM,WAAW,CAAC;AAMnB,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAG3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGjD,YAAY,EACV,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,SAAS,EACT,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,EACb,cAAc,EACd,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,wBAAwB,EACxB,eAAe,EACf,yBAAyB,EACzB,sBAAsB,EACtB,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,0BAA0B,EAC1B,uBAAuB,EACvB,0BAA0B,EAC1B,gBAAgB,EAChB,mBAAmB,EACnB,+BAA+B,EAC/B,kCAAkC,GACnC,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8Cb,CAAC"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * @peac/net-node Testing Utilities
3
+ *
4
+ * This module exports internal utilities for testing purposes only.
5
+ * These APIs are NOT part of the public contract and may change without notice.
6
+ *
7
+ * Import via:
8
+ * import { _internals } from '@peac/net-node/testing';
9
+ *
10
+ * DO NOT use these in production code.
11
+ *
12
+ * @module @peac/net-node/testing
13
+ */
14
+ // Import internal functions from impl.ts (the internal implementation module)
15
+ import {
16
+ // Core validation
17
+ isPrivateIP, resolveDnsSecure, createPinnedAgent,
18
+ // CIDR utilities
19
+ parseCidr, matchesAnyCidr, validateAllowCidrsAck, cidrOverlapsDangerousRanges,
20
+ // URL/hostname utilities
21
+ getRegistrableDomain, isRedirectAllowed, normalizeHostname, sanitizeUrl,
22
+ // Response handling
23
+ readBodyWithLimit, attemptWithFallback,
24
+ // Header utilities
25
+ hasHeaderCaseInsensitive, stripHopByHopHeaders,
26
+ // Canonical host pipeline
27
+ hasIPv6ZoneId, normalizeIPv4MappedIPv6, canonicalizeHost,
28
+ // Audit helpers
29
+ emitAuditEvent, createRequestAuditContext,
30
+ // Evidence redaction helpers
31
+ hashIpAddress, redactIp,
32
+ // Audit queue helpers (for testing)
33
+ resetAuditQueueStats,
34
+ // Constants (read-only references)
35
+ BLOCKED_IPV4_RANGES, BLOCKED_IPV6_RANGES, ADDITIONAL_BLOCKED_CIDRS_V4, ADDITIONAL_BLOCKED_CIDRS_V6, HOP_BY_HOP_HEADERS, } from './impl.js';
36
+ // Import schema version constants from index.ts (public API)
37
+ import { SAFE_FETCH_EVENT_SCHEMA_VERSION, SAFE_FETCH_EVIDENCE_SCHEMA_VERSION } from './index.js';
38
+ // Import JCS canonicalization helper from evidence-utils
39
+ import { jcsCanonicalizeValue } from './evidence-utils.js';
40
+ // Import finalizeEvidence from internal module (NOT in main entry)
41
+ import { finalizeEvidence } from './internal.js';
42
+ // Re-export public utilities that are useful for testing
43
+ export { canonicalizeEvidence, computeEvidenceDigest, getAuditQueueStats, MAX_PENDING_AUDIT_EVENTS, DANGEROUS_PORTS, ALLOW_DANGEROUS_PORTS_ACK, SAFE_FETCH_ERROR_CODES, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_REDIRECTS, DEFAULT_ALLOWED_METHODS, DEFAULT_MAX_RESPONSE_BYTES, MAX_JWKS_RESPONSE_BYTES, RESPONSE_BYTES_MEASUREMENT, DEFAULT_TIMEOUTS, ALLOW_MIXED_DNS_ACK, SAFE_FETCH_EVENT_SCHEMA_VERSION, SAFE_FETCH_EVIDENCE_SCHEMA_VERSION, } from './index.js';
44
+ /**
45
+ * Internal utilities for testing.
46
+ *
47
+ * These utilities provide access to internal implementation details
48
+ * for testing purposes only. They are NOT stable APIs.
49
+ *
50
+ * @example
51
+ * import { _internals } from '@peac/net-node/testing';
52
+ *
53
+ * // Access internal functions
54
+ * const isPrivate = _internals.isPrivateIP('10.0.0.1');
55
+ */
56
+ export const _internals = {
57
+ // Core validation
58
+ isPrivateIP,
59
+ resolveDnsSecure,
60
+ createPinnedAgent,
61
+ // CIDR utilities
62
+ parseCidr,
63
+ matchesAnyCidr,
64
+ validateAllowCidrsAck,
65
+ cidrOverlapsDangerousRanges,
66
+ // URL/hostname utilities
67
+ getRegistrableDomain,
68
+ isRedirectAllowed,
69
+ normalizeHostname,
70
+ sanitizeUrl,
71
+ // Response handling
72
+ readBodyWithLimit,
73
+ attemptWithFallback,
74
+ // Header utilities
75
+ hasHeaderCaseInsensitive,
76
+ stripHopByHopHeaders,
77
+ // Canonical host pipeline
78
+ hasIPv6ZoneId,
79
+ normalizeIPv4MappedIPv6,
80
+ canonicalizeHost,
81
+ // Audit helpers
82
+ emitAuditEvent,
83
+ createRequestAuditContext,
84
+ // Evidence redaction helpers
85
+ hashIpAddress,
86
+ redactIp,
87
+ // JCS canonicalization helpers
88
+ jcsCanonicalizeValue,
89
+ // Audit queue helpers (for testing)
90
+ resetAuditQueueStats,
91
+ // Evidence finalization (from internal.ts - NOT in main entry)
92
+ finalizeEvidence,
93
+ // Constants (read-only references)
94
+ BLOCKED_IPV4_RANGES,
95
+ BLOCKED_IPV6_RANGES,
96
+ ADDITIONAL_BLOCKED_CIDRS_V4,
97
+ ADDITIONAL_BLOCKED_CIDRS_V6,
98
+ HOP_BY_HOP_HEADERS,
99
+ // Schema version constants
100
+ SAFE_FETCH_EVENT_SCHEMA_VERSION,
101
+ SAFE_FETCH_EVIDENCE_SCHEMA_VERSION,
102
+ };
103
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,8EAA8E;AAC9E,OAAO;AACL,kBAAkB;AAClB,WAAW,EACX,gBAAgB,EAChB,iBAAiB;AACjB,iBAAiB;AACjB,SAAS,EACT,cAAc,EACd,qBAAqB,EACrB,2BAA2B;AAC3B,yBAAyB;AACzB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,WAAW;AACX,oBAAoB;AACpB,iBAAiB,EACjB,mBAAmB;AACnB,mBAAmB;AACnB,wBAAwB,EACxB,oBAAoB;AACpB,0BAA0B;AAC1B,aAAa,EACb,uBAAuB,EACvB,gBAAgB;AAChB,gBAAgB;AAChB,cAAc,EACd,yBAAyB;AACzB,6BAA6B;AAC7B,aAAa,EACb,QAAQ;AACR,oCAAoC;AACpC,oBAAoB;AACpB,mCAAmC;AACnC,mBAAmB,EACnB,mBAAmB,EACnB,2BAA2B,EAC3B,2BAA2B,EAC3B,kBAAkB,GACnB,MAAM,WAAW,CAAC;AAEnB,6DAA6D;AAC7D,OAAO,EAAE,+BAA+B,EAAE,kCAAkC,EAAE,MAAM,YAAY,CAAC;AAEjG,yDAAyD;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE3D,mEAAmE;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAiBjD,yDAAyD;AACzD,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,wBAAwB,EACxB,eAAe,EACf,yBAAyB,EACzB,sBAAsB,EACtB,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,0BAA0B,EAC1B,uBAAuB,EACvB,0BAA0B,EAC1B,gBAAgB,EAChB,mBAAmB,EACnB,+BAA+B,EAC/B,kCAAkC,GACnC,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,kBAAkB;IAClB,WAAW;IACX,gBAAgB;IAChB,iBAAiB;IACjB,iBAAiB;IACjB,SAAS;IACT,cAAc;IACd,qBAAqB;IACrB,2BAA2B;IAC3B,yBAAyB;IACzB,oBAAoB;IACpB,iBAAiB;IACjB,iBAAiB;IACjB,WAAW;IACX,oBAAoB;IACpB,iBAAiB;IACjB,mBAAmB;IACnB,mBAAmB;IACnB,wBAAwB;IACxB,oBAAoB;IACpB,0BAA0B;IAC1B,aAAa;IACb,uBAAuB;IACvB,gBAAgB;IAChB,gBAAgB;IAChB,cAAc;IACd,yBAAyB;IACzB,6BAA6B;IAC7B,aAAa;IACb,QAAQ;IACR,+BAA+B;IAC/B,oBAAoB;IACpB,oCAAoC;IACpC,oBAAoB;IACpB,+DAA+D;IAC/D,gBAAgB;IAChB,mCAAmC;IACnC,mBAAmB;IACnB,mBAAmB;IACnB,2BAA2B;IAC3B,2BAA2B;IAC3B,kBAAkB;IAClB,2BAA2B;IAC3B,+BAA+B;IAC/B,kCAAkC;CAC1B,CAAC"}
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@peac/net-node",
3
+ "version": "0.12.6",
4
+ "description": "SSRF-safe network utilities for PEAC Protocol with DNS resolution pinning (Node.js only)",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./testing": {
14
+ "types": "./dist/testing.d.ts",
15
+ "import": "./dist/testing.js"
16
+ },
17
+ "./*": null
18
+ },
19
+ "engines": {
20
+ "node": ">=22.0.0"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/peacprotocol/peac.git",
25
+ "directory": "packages/net/node"
26
+ },
27
+ "author": "PEAC Protocol Contributors",
28
+ "license": "Apache-2.0",
29
+ "bugs": {
30
+ "url": "https://github.com/peacprotocol/peac/issues"
31
+ },
32
+ "homepage": "https://github.com/peacprotocol/peac#readme",
33
+ "files": [
34
+ "dist",
35
+ "README.md",
36
+ "LICENSE"
37
+ ],
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "dependencies": {
42
+ "ipaddr.js": "^2.3.0",
43
+ "tldts": "^6.1.86",
44
+ "undici": "^6.24.0",
45
+ "@peac/schema": "0.12.6"
46
+ },
47
+ "devDependencies": {
48
+ "@arethetypeswrong/cli": "^0.18.2",
49
+ "@types/node": "^22.19.11",
50
+ "publint": "^0.3.17",
51
+ "typescript": "^5.3.3",
52
+ "vitest": "^4.0.0"
53
+ },
54
+ "keywords": [
55
+ "peac",
56
+ "peacprotocol",
57
+ "interaction-records",
58
+ "signed-records",
59
+ "receipts",
60
+ "originary",
61
+ "network",
62
+ "node",
63
+ "jwks-resolution",
64
+ "ssrf-safe"
65
+ ],
66
+ "scripts": {
67
+ "build": "tsc",
68
+ "lint": "echo 'Run pnpm lint from repo root'",
69
+ "test": "vitest run",
70
+ "test:watch": "vitest",
71
+ "clean": "rm -rf dist",
72
+ "verify:jcs-vectors": "node scripts/verify-jcs-vectors.mjs",
73
+ "regen:jcs-vectors": "node scripts/verify-jcs-vectors.mjs --regenerate",
74
+ "test:pack-install": "node scripts/test-pack-install.mjs",
75
+ "lint:pkg": "publint && attw --pack . --ignore-rules cjs-resolves-to-esm no-resolution",
76
+ "lint:publint": "publint",
77
+ "lint:attw": "attw --pack . --ignore-rules cjs-resolves-to-esm no-resolution"
78
+ }
79
+ }