@sip-protocol/sdk 0.7.3 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +267 -0
- package/dist/{TransportWebUSB-TQ7WZ4LE.mjs → TransportWebUSB-YQMAGJAJ.mjs} +12 -9
- package/dist/browser.d.mts +10 -4
- package/dist/browser.d.ts +10 -4
- package/dist/browser.js +47556 -19603
- package/dist/browser.mjs +628 -48
- package/dist/chunk-4GRJ5MAW.mjs +152 -0
- package/dist/chunk-5D7A3L3W.mjs +717 -0
- package/dist/chunk-64AYA5F5.mjs +7834 -0
- package/dist/chunk-GMDGB22A.mjs +379 -0
- package/dist/chunk-I534WKN7.mjs +328 -0
- package/dist/chunk-IBZVA5Y7.mjs +1003 -0
- package/dist/chunk-PRRZAWJE.mjs +223 -0
- package/dist/{chunk-UJCSKKID.mjs → chunk-XGB3TDIC.mjs} +13 -1
- package/dist/{chunk-3M3HNQCW.mjs → chunk-YWGJ77A2.mjs} +28656 -13103
- package/dist/{chunk-6WGN57S2.mjs → chunk-Z3K7W5S3.mjs} +48 -0
- package/dist/constants-LHAAUC2T.mjs +51 -0
- package/dist/dist-2OGQ7FED.mjs +3957 -0
- package/dist/dist-IFHPYLDX.mjs +254 -0
- package/dist/fulfillment_proof-ANHVPKTB.mjs +21 -0
- package/dist/funding_proof-ICFZ5LHY.mjs +21 -0
- package/dist/{index-DIBZHOOQ.d.ts → index-DXh2IGkz.d.ts} +21239 -10304
- package/dist/{index-8MQz13eJ.d.mts → index-DeE1ZzA4.d.mts} +21239 -10304
- package/dist/index.d.mts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +48396 -19623
- package/dist/index.mjs +537 -19
- package/dist/interface-Bf7w1PLW.d.mts +679 -0
- package/dist/interface-Bf7w1PLW.d.ts +679 -0
- package/dist/{noir-DKfEzWy9.d.mts → noir-kzbLVTei.d.mts} +31 -21
- package/dist/{noir-DKfEzWy9.d.ts → noir-kzbLVTei.d.ts} +31 -21
- package/dist/proofs/halo2.d.mts +151 -0
- package/dist/proofs/halo2.d.ts +151 -0
- package/dist/proofs/halo2.js +350 -0
- package/dist/proofs/halo2.mjs +11 -0
- package/dist/proofs/kimchi.d.mts +160 -0
- package/dist/proofs/kimchi.d.ts +160 -0
- package/dist/proofs/kimchi.js +431 -0
- package/dist/proofs/kimchi.mjs +13 -0
- package/dist/proofs/noir.d.mts +1 -1
- package/dist/proofs/noir.d.ts +1 -1
- package/dist/proofs/noir.js +74 -18
- package/dist/proofs/noir.mjs +84 -24
- package/dist/solana-U3MEGU7W.mjs +280 -0
- package/dist/validity_proof-3POXLPNY.mjs +21 -0
- package/package.json +54 -21
- package/src/adapters/index.ts +41 -0
- package/src/adapters/jupiter.ts +571 -0
- package/src/adapters/near-intents.ts +135 -0
- package/src/advisor/advisor.ts +653 -0
- package/src/advisor/index.ts +54 -0
- package/src/advisor/tools.ts +303 -0
- package/src/advisor/types.ts +164 -0
- package/src/chains/ethereum/announcement.ts +536 -0
- package/src/chains/ethereum/bnb-optimizations.ts +474 -0
- package/src/chains/ethereum/commitment.ts +522 -0
- package/src/chains/ethereum/constants.ts +462 -0
- package/src/chains/ethereum/deployment.ts +596 -0
- package/src/chains/ethereum/gas-estimation.ts +538 -0
- package/src/chains/ethereum/index.ts +268 -0
- package/src/chains/ethereum/optimizations.ts +614 -0
- package/src/chains/ethereum/privacy-adapter.ts +855 -0
- package/src/chains/ethereum/registry.ts +584 -0
- package/src/chains/ethereum/rpc.ts +905 -0
- package/src/chains/ethereum/stealth.ts +491 -0
- package/src/chains/ethereum/token.ts +790 -0
- package/src/chains/ethereum/transfer.ts +637 -0
- package/src/chains/ethereum/types.ts +456 -0
- package/src/chains/ethereum/viewing-key.ts +455 -0
- package/src/chains/near/commitment.ts +608 -0
- package/src/chains/near/constants.ts +284 -0
- package/src/chains/near/function-call.ts +871 -0
- package/src/chains/near/history.ts +654 -0
- package/src/chains/near/implicit-account.ts +840 -0
- package/src/chains/near/index.ts +393 -0
- package/src/chains/near/native-transfer.ts +658 -0
- package/src/chains/near/nep141.ts +775 -0
- package/src/chains/near/privacy-adapter.ts +889 -0
- package/src/chains/near/resolver.ts +971 -0
- package/src/chains/near/rpc.ts +1016 -0
- package/src/chains/near/stealth.ts +419 -0
- package/src/chains/near/types.ts +317 -0
- package/src/chains/near/viewing-key.ts +876 -0
- package/src/chains/solana/anchor-transfer.ts +386 -0
- package/src/chains/solana/commitment.ts +577 -0
- package/src/chains/solana/constants.ts +126 -12
- package/src/chains/solana/ephemeral-keys.ts +543 -0
- package/src/chains/solana/index.ts +252 -1
- package/src/chains/solana/key-derivation.ts +418 -0
- package/src/chains/solana/kit-compat.ts +334 -0
- package/src/chains/solana/optimizations.ts +560 -0
- package/src/chains/solana/privacy-adapter.ts +605 -0
- package/src/chains/solana/providers/generic.ts +47 -6
- package/src/chains/solana/providers/helius-enhanced-types.ts +336 -0
- package/src/chains/solana/providers/helius-enhanced.ts +623 -0
- package/src/chains/solana/providers/helius.ts +186 -33
- package/src/chains/solana/providers/index.ts +31 -0
- package/src/chains/solana/providers/interface.ts +61 -18
- package/src/chains/solana/providers/quicknode.ts +409 -0
- package/src/chains/solana/providers/triton.ts +426 -0
- package/src/chains/solana/providers/webhook.ts +338 -67
- package/src/chains/solana/rpc-client.ts +1150 -0
- package/src/chains/solana/scan.ts +83 -66
- package/src/chains/solana/sol-transfer.ts +732 -0
- package/src/chains/solana/spl-transfer.ts +886 -0
- package/src/chains/solana/stealth-scanner.ts +703 -0
- package/src/chains/solana/sunspot-verifier.ts +453 -0
- package/src/chains/solana/transaction-builder.ts +755 -0
- package/src/chains/solana/transfer.ts +74 -5
- package/src/chains/solana/types.ts +57 -6
- package/src/chains/solana/utils.ts +110 -0
- package/src/chains/solana/viewing-key.ts +807 -0
- package/src/compliance/fireblocks.ts +921 -0
- package/src/compliance/index.ts +23 -0
- package/src/compliance/range-sas.ts +398 -33
- package/src/config/endpoints.ts +100 -0
- package/src/crypto.ts +11 -8
- package/src/errors.ts +82 -0
- package/src/evm/erc4337-relayer.ts +830 -0
- package/src/evm/index.ts +47 -0
- package/src/fees/calculator.ts +396 -0
- package/src/fees/index.ts +87 -0
- package/src/fees/near-contract.ts +429 -0
- package/src/fees/types.ts +268 -0
- package/src/index.ts +686 -1
- package/src/intent.ts +6 -3
- package/src/logger.ts +324 -0
- package/src/network/index.ts +80 -0
- package/src/network/proxy.ts +691 -0
- package/src/optimizations/index.ts +541 -0
- package/src/oracle/types.ts +1 -0
- package/src/privacy-backends/arcium-types.ts +727 -0
- package/src/privacy-backends/arcium.ts +719 -0
- package/src/privacy-backends/combined-privacy.ts +866 -0
- package/src/privacy-backends/cspl-token.ts +595 -0
- package/src/privacy-backends/cspl-types.ts +512 -0
- package/src/privacy-backends/cspl.ts +907 -0
- package/src/privacy-backends/health.ts +488 -0
- package/src/privacy-backends/inco-types.ts +323 -0
- package/src/privacy-backends/inco.ts +616 -0
- package/src/privacy-backends/index.ts +254 -4
- package/src/privacy-backends/interface.ts +649 -6
- package/src/privacy-backends/lru-cache.ts +343 -0
- package/src/privacy-backends/magicblock.ts +458 -0
- package/src/privacy-backends/mock.ts +258 -0
- package/src/privacy-backends/privacycash.ts +13 -17
- package/src/privacy-backends/private-swap.ts +570 -0
- package/src/privacy-backends/rate-limiter.ts +683 -0
- package/src/privacy-backends/registry.ts +414 -2
- package/src/privacy-backends/router.ts +283 -3
- package/src/privacy-backends/shadowwire.ts +449 -0
- package/src/privacy-backends/sip-native.ts +3 -0
- package/src/privacy-logger.ts +191 -0
- package/src/production-safety.ts +373 -0
- package/src/proofs/aggregator.ts +1029 -0
- package/src/proofs/browser-composer.ts +1150 -0
- package/src/proofs/browser.ts +113 -25
- package/src/proofs/cache/index.ts +127 -0
- package/src/proofs/cache/interface.ts +545 -0
- package/src/proofs/cache/key-generator.ts +188 -0
- package/src/proofs/cache/lru-cache.ts +481 -0
- package/src/proofs/cache/multi-tier-cache.ts +575 -0
- package/src/proofs/cache/persistent-cache.ts +788 -0
- package/src/proofs/compliance-proof.ts +872 -0
- package/src/proofs/composer/base.ts +923 -0
- package/src/proofs/composer/index.ts +25 -0
- package/src/proofs/composer/interface.ts +518 -0
- package/src/proofs/composer/types.ts +383 -0
- package/src/proofs/converters/halo2.ts +452 -0
- package/src/proofs/converters/index.ts +208 -0
- package/src/proofs/converters/interface.ts +363 -0
- package/src/proofs/converters/kimchi.ts +462 -0
- package/src/proofs/converters/noir.ts +451 -0
- package/src/proofs/fallback.ts +888 -0
- package/src/proofs/halo2.ts +42 -0
- package/src/proofs/index.ts +471 -0
- package/src/proofs/interface.ts +13 -0
- package/src/proofs/kimchi.ts +42 -0
- package/src/proofs/lazy.ts +1004 -0
- package/src/proofs/mock.ts +25 -1
- package/src/proofs/noir.ts +110 -29
- package/src/proofs/orchestrator.ts +960 -0
- package/src/proofs/parallel/concurrency.ts +297 -0
- package/src/proofs/parallel/dependency-graph.ts +602 -0
- package/src/proofs/parallel/executor.ts +420 -0
- package/src/proofs/parallel/index.ts +131 -0
- package/src/proofs/parallel/interface.ts +685 -0
- package/src/proofs/parallel/worker-pool.ts +644 -0
- package/src/proofs/providers/halo2.ts +560 -0
- package/src/proofs/providers/index.ts +34 -0
- package/src/proofs/providers/kimchi.ts +641 -0
- package/src/proofs/validator.ts +881 -0
- package/src/proofs/verifier.ts +867 -0
- package/src/quantum/index.ts +112 -0
- package/src/quantum/winternitz-vault.ts +639 -0
- package/src/quantum/wots.ts +611 -0
- package/src/settlement/backends/direct-chain.ts +1 -0
- package/src/settlement/index.ts +9 -0
- package/src/settlement/router.ts +732 -46
- package/src/solana/index.ts +72 -0
- package/src/solana/jito-relayer.ts +687 -0
- package/src/solana/noir-verifier-types.ts +430 -0
- package/src/solana/noir-verifier.ts +816 -0
- package/src/stealth/address-derivation.ts +193 -0
- package/src/stealth/ed25519.ts +431 -0
- package/src/stealth/index.ts +233 -0
- package/src/stealth/meta-address.ts +221 -0
- package/src/stealth/secp256k1.ts +368 -0
- package/src/stealth/utils.ts +194 -0
- package/src/stealth.ts +50 -1504
- package/src/sync/index.ts +106 -0
- package/src/sync/manager.ts +504 -0
- package/src/sync/mock-provider.ts +318 -0
- package/src/sync/oblivious.ts +625 -0
- package/src/tokens/index.ts +15 -0
- package/src/tokens/registry.ts +301 -0
- package/src/utils/deprecation.ts +94 -0
- package/src/utils/index.ts +9 -0
- package/src/wallet/ethereum/index.ts +68 -0
- package/src/wallet/ethereum/metamask-privacy.ts +420 -0
- package/src/wallet/ethereum/multi-wallet.ts +646 -0
- package/src/wallet/ethereum/privacy-adapter.ts +700 -0
- package/src/wallet/ethereum/types.ts +3 -1
- package/src/wallet/ethereum/walletconnect-adapter.ts +675 -0
- package/src/wallet/hardware/index.ts +10 -0
- package/src/wallet/hardware/ledger-privacy.ts +414 -0
- package/src/wallet/index.ts +71 -0
- package/src/wallet/near/adapter.ts +626 -0
- package/src/wallet/near/index.ts +86 -0
- package/src/wallet/near/meteor-wallet.ts +1153 -0
- package/src/wallet/near/my-near-wallet.ts +790 -0
- package/src/wallet/near/wallet-selector.ts +702 -0
- package/src/wallet/solana/adapter.ts +6 -4
- package/src/wallet/solana/index.ts +13 -0
- package/src/wallet/solana/privacy-adapter.ts +567 -0
- package/src/wallet/sui/types.ts +6 -4
- package/src/zcash/rpc-client.ts +13 -6
- package/dist/chunk-2XIVXWHA.mjs +0 -1930
- package/dist/chunk-3INS3PR5.mjs +0 -884
- package/dist/chunk-3OVABDRH.mjs +0 -17096
- package/dist/chunk-7RFRWDCW.mjs +0 -1504
- package/dist/chunk-DLDWZFYC.mjs +0 -1495
- package/dist/chunk-E6SZWREQ.mjs +0 -57
- package/dist/chunk-F6F73W35.mjs +0 -16166
- package/dist/chunk-G33LB27A.mjs +0 -16166
- package/dist/chunk-HGU6HZRC.mjs +0 -231
- package/dist/chunk-L2K34JCU.mjs +0 -1496
- package/dist/chunk-OFDBEIEK.mjs +0 -16166
- package/dist/chunk-SF7YSLF5.mjs +0 -1515
- package/dist/chunk-SN4ZDTVW.mjs +0 -16166
- package/dist/chunk-WWUSGOXE.mjs +0 -17129
- package/dist/constants-VOI7BSLK.mjs +0 -27
- package/dist/index-B71aXVzk.d.ts +0 -13264
- package/dist/index-BYZbDjal.d.ts +0 -11390
- package/dist/index-CHB3KuOB.d.mts +0 -11859
- package/dist/index-CzWPI6Le.d.ts +0 -11859
- package/dist/index-pOIIuwfV.d.mts +0 -13264
- package/dist/index-xbWjohNq.d.mts +0 -11390
- package/dist/solana-4O4K45VU.mjs +0 -46
- package/dist/solana-5EMCTPTS.mjs +0 -46
- package/dist/solana-NDABAZ6P.mjs +0 -56
- package/dist/solana-Q4NAVBTS.mjs +0 -46
- package/dist/solana-ZYO63LY5.mjs +0 -46
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Production Safety Checks
|
|
3
|
+
*
|
|
4
|
+
* Runtime validation to detect development-only configurations in production.
|
|
5
|
+
* Prevents accidental localhost URLs, default credentials, and other dev-only
|
|
6
|
+
* settings from being used in production deployments.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { validateProductionConfig, isProductionEnvironment } from '@sip-protocol/sdk'
|
|
11
|
+
*
|
|
12
|
+
* // Check if running in production
|
|
13
|
+
* if (isProductionEnvironment()) {
|
|
14
|
+
* // Validate config throws if localhost URLs detected
|
|
15
|
+
* validateProductionConfig({
|
|
16
|
+
* rpcEndpoint: process.env.RPC_ENDPOINT,
|
|
17
|
+
* apiUrl: process.env.API_URL,
|
|
18
|
+
* })
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* // Or use assertNoLocalhost for individual URLs
|
|
22
|
+
* const endpoint = assertNoLocalhost(
|
|
23
|
+
* process.env.RPC_ENDPOINT || 'http://localhost:8899',
|
|
24
|
+
* 'RPC_ENDPOINT'
|
|
25
|
+
* )
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
// ─── Constants ──────────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Patterns that indicate localhost/development URLs
|
|
33
|
+
*/
|
|
34
|
+
const LOCALHOST_PATTERNS = [
|
|
35
|
+
/^https?:\/\/localhost(:\d+)?/i,
|
|
36
|
+
/^https?:\/\/127\.0\.0\.1(:\d+)?/i,
|
|
37
|
+
/^https?:\/\/0\.0\.0\.0(:\d+)?/i,
|
|
38
|
+
/^https?:\/\/\[::1\](:\d+)?/i,
|
|
39
|
+
/^https?:\/\/host\.docker\.internal(:\d+)?/i,
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Environment variable that can override localhost safety checks
|
|
44
|
+
*/
|
|
45
|
+
const ALLOW_LOCALHOST_ENV = 'SIP_ALLOW_LOCALHOST_IN_PROD'
|
|
46
|
+
|
|
47
|
+
// ─── Environment Detection ──────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Check if running in a production environment
|
|
51
|
+
*
|
|
52
|
+
* Production is detected when:
|
|
53
|
+
* - NODE_ENV === 'production'
|
|
54
|
+
* - SIP_ENV === 'production'
|
|
55
|
+
*
|
|
56
|
+
* @returns true if production environment detected
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* if (isProductionEnvironment()) {
|
|
61
|
+
* console.log('Running in production mode')
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export function isProductionEnvironment(): boolean {
|
|
66
|
+
if (typeof process === 'undefined' || !process.env) {
|
|
67
|
+
// Browser without process - check window location
|
|
68
|
+
if (typeof window !== 'undefined' && window.location) {
|
|
69
|
+
const hostname = window.location.hostname
|
|
70
|
+
// Not localhost = likely production
|
|
71
|
+
return !isLocalhostUrl(`https://${hostname}`)
|
|
72
|
+
}
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const nodeEnv = process.env.NODE_ENV?.toLowerCase()
|
|
77
|
+
const sipEnv = process.env.SIP_ENV?.toLowerCase()
|
|
78
|
+
|
|
79
|
+
return nodeEnv === 'production' || sipEnv === 'production'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Check if localhost URLs are explicitly allowed in production
|
|
84
|
+
*
|
|
85
|
+
* @returns true if SIP_ALLOW_LOCALHOST_IN_PROD=true
|
|
86
|
+
*/
|
|
87
|
+
export function isLocalhostAllowed(): boolean {
|
|
88
|
+
if (typeof process === 'undefined' || !process.env) {
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
return process.env[ALLOW_LOCALHOST_ENV] === 'true'
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ─── URL Validation ─────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Check if a URL points to localhost
|
|
98
|
+
*
|
|
99
|
+
* @param url - URL to check
|
|
100
|
+
* @returns true if URL is localhost
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* isLocalhostUrl('http://localhost:8899') // true
|
|
105
|
+
* isLocalhostUrl('https://api.mainnet.solana.com') // false
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export function isLocalhostUrl(url: string): boolean {
|
|
109
|
+
return LOCALHOST_PATTERNS.some((pattern) => pattern.test(url))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Result from validateProductionConfig
|
|
114
|
+
*/
|
|
115
|
+
export interface ProductionConfigValidationResult {
|
|
116
|
+
valid: boolean
|
|
117
|
+
errors: ProductionConfigError[]
|
|
118
|
+
warnings: ProductionConfigWarning[]
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Error found during production config validation
|
|
123
|
+
*/
|
|
124
|
+
export interface ProductionConfigError {
|
|
125
|
+
key: string
|
|
126
|
+
value: string
|
|
127
|
+
message: string
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Warning found during production config validation
|
|
132
|
+
*/
|
|
133
|
+
export interface ProductionConfigWarning {
|
|
134
|
+
key: string
|
|
135
|
+
message: string
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Error thrown when production validation fails
|
|
140
|
+
*/
|
|
141
|
+
export class ProductionSafetyError extends Error {
|
|
142
|
+
constructor(
|
|
143
|
+
message: string,
|
|
144
|
+
public readonly errors: ProductionConfigError[]
|
|
145
|
+
) {
|
|
146
|
+
super(message)
|
|
147
|
+
this.name = 'ProductionSafetyError'
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Validate configuration for production safety
|
|
153
|
+
*
|
|
154
|
+
* In production mode:
|
|
155
|
+
* - Throws if any URL value contains localhost
|
|
156
|
+
* - Can be bypassed with SIP_ALLOW_LOCALHOST_IN_PROD=true
|
|
157
|
+
*
|
|
158
|
+
* In non-production mode:
|
|
159
|
+
* - Returns validation result without throwing
|
|
160
|
+
* - Logs warnings for localhost URLs
|
|
161
|
+
*
|
|
162
|
+
* @param config - Configuration object to validate (key-value pairs)
|
|
163
|
+
* @param options - Validation options
|
|
164
|
+
* @returns Validation result with errors and warnings
|
|
165
|
+
* @throws ProductionSafetyError if production mode and localhost URLs found
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* // Validates all URL-like values in config
|
|
170
|
+
* validateProductionConfig({
|
|
171
|
+
* rpcEndpoint: 'http://localhost:8899', // Error in production
|
|
172
|
+
* apiUrl: 'https://api.example.com', // OK
|
|
173
|
+
* name: 'my-app', // Ignored (not a URL)
|
|
174
|
+
* })
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
export function validateProductionConfig(
|
|
178
|
+
config: Record<string, unknown>,
|
|
179
|
+
options: {
|
|
180
|
+
/** Only validate these keys (defaults to all string values that look like URLs) */
|
|
181
|
+
keys?: string[]
|
|
182
|
+
/** Throw in production even if localhost allowed (for critical configs) */
|
|
183
|
+
strict?: boolean
|
|
184
|
+
} = {}
|
|
185
|
+
): ProductionConfigValidationResult {
|
|
186
|
+
const isProduction = isProductionEnvironment()
|
|
187
|
+
const localhostAllowed = isLocalhostAllowed()
|
|
188
|
+
const errors: ProductionConfigError[] = []
|
|
189
|
+
const warnings: ProductionConfigWarning[] = []
|
|
190
|
+
|
|
191
|
+
// Determine which keys to validate
|
|
192
|
+
const keysToValidate = options.keys ?? Object.keys(config)
|
|
193
|
+
|
|
194
|
+
for (const key of keysToValidate) {
|
|
195
|
+
const value = config[key]
|
|
196
|
+
|
|
197
|
+
// Skip non-string values
|
|
198
|
+
if (typeof value !== 'string') {
|
|
199
|
+
continue
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Skip values that don't look like URLs
|
|
203
|
+
if (!value.startsWith('http://') && !value.startsWith('https://')) {
|
|
204
|
+
continue
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Check for localhost
|
|
208
|
+
if (isLocalhostUrl(value)) {
|
|
209
|
+
if (isProduction && !localhostAllowed) {
|
|
210
|
+
errors.push({
|
|
211
|
+
key,
|
|
212
|
+
value: maskSensitiveUrl(value),
|
|
213
|
+
message: `Localhost URL detected in production for '${key}'. Set a production URL or ${ALLOW_LOCALHOST_ENV}=true to override.`,
|
|
214
|
+
})
|
|
215
|
+
} else if (isProduction && localhostAllowed && options.strict) {
|
|
216
|
+
errors.push({
|
|
217
|
+
key,
|
|
218
|
+
value: maskSensitiveUrl(value),
|
|
219
|
+
message: `Localhost URL not allowed for '${key}' even with ${ALLOW_LOCALHOST_ENV}=true (strict mode).`,
|
|
220
|
+
})
|
|
221
|
+
} else if (isProduction) {
|
|
222
|
+
warnings.push({
|
|
223
|
+
key,
|
|
224
|
+
message: `Using localhost URL for '${key}' in production (allowed via ${ALLOW_LOCALHOST_ENV})`,
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const result: ProductionConfigValidationResult = {
|
|
231
|
+
valid: errors.length === 0,
|
|
232
|
+
errors,
|
|
233
|
+
warnings,
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Throw in production if errors found
|
|
237
|
+
if (!result.valid && isProduction) {
|
|
238
|
+
const errorMessages = errors.map((e) => ` - ${e.key}: ${e.message}`).join('\n')
|
|
239
|
+
throw new ProductionSafetyError(
|
|
240
|
+
`Production safety check failed:\n${errorMessages}`,
|
|
241
|
+
errors
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return result
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Assert that a URL is not localhost in production
|
|
250
|
+
*
|
|
251
|
+
* Convenience function for validating individual URLs.
|
|
252
|
+
*
|
|
253
|
+
* @param url - URL to validate
|
|
254
|
+
* @param name - Name of the config key (for error messages)
|
|
255
|
+
* @returns The URL if valid
|
|
256
|
+
* @throws ProductionSafetyError if localhost in production
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```typescript
|
|
260
|
+
* // Throws in production if localhost
|
|
261
|
+
* const endpoint = assertNoLocalhost(
|
|
262
|
+
* process.env.RPC_ENDPOINT || 'http://localhost:8899',
|
|
263
|
+
* 'RPC_ENDPOINT'
|
|
264
|
+
* )
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
export function assertNoLocalhost(url: string, name: string): string {
|
|
268
|
+
validateProductionConfig({ [name]: url }, { keys: [name] })
|
|
269
|
+
return url
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Get a URL with production fallback
|
|
274
|
+
*
|
|
275
|
+
* In production:
|
|
276
|
+
* - If primary is localhost, throws error
|
|
277
|
+
* - Returns primary if valid
|
|
278
|
+
*
|
|
279
|
+
* In development:
|
|
280
|
+
* - Returns primary (even if localhost)
|
|
281
|
+
*
|
|
282
|
+
* @param primary - Primary URL (may be localhost in dev)
|
|
283
|
+
* @param name - Name of the config key
|
|
284
|
+
* @returns Valid URL for the current environment
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* ```typescript
|
|
288
|
+
* const rpcEndpoint = getProductionUrl(
|
|
289
|
+
* process.env.RPC_ENDPOINT || 'http://localhost:8899',
|
|
290
|
+
* 'RPC_ENDPOINT'
|
|
291
|
+
* )
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
294
|
+
export function getProductionUrl(primary: string, name: string): string {
|
|
295
|
+
if (isProductionEnvironment() && isLocalhostUrl(primary) && !isLocalhostAllowed()) {
|
|
296
|
+
throw new ProductionSafetyError(
|
|
297
|
+
`No production URL configured for '${name}'. Current value: ${maskSensitiveUrl(primary)}`,
|
|
298
|
+
[{ key: name, value: maskSensitiveUrl(primary), message: 'Localhost URL in production' }]
|
|
299
|
+
)
|
|
300
|
+
}
|
|
301
|
+
return primary
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// ─── Utility Functions ──────────────────────────────────────────────────────────
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Mask sensitive parts of a URL for safe logging
|
|
308
|
+
*
|
|
309
|
+
* @param url - URL to mask
|
|
310
|
+
* @returns URL with credentials and API keys masked
|
|
311
|
+
*/
|
|
312
|
+
function maskSensitiveUrl(url: string): string {
|
|
313
|
+
try {
|
|
314
|
+
const parsed = new URL(url)
|
|
315
|
+
|
|
316
|
+
// Mask credentials
|
|
317
|
+
if (parsed.username || parsed.password) {
|
|
318
|
+
parsed.username = '***'
|
|
319
|
+
parsed.password = ''
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Mask API keys in query params
|
|
323
|
+
const sensitiveParams = ['api-key', 'apikey', 'key', 'token', 'secret']
|
|
324
|
+
for (const param of sensitiveParams) {
|
|
325
|
+
if (parsed.searchParams.has(param)) {
|
|
326
|
+
parsed.searchParams.set(param, '***')
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return parsed.toString()
|
|
331
|
+
} catch {
|
|
332
|
+
// If URL parsing fails, return as-is (it's likely just localhost anyway)
|
|
333
|
+
return url
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Create a production-safe configuration helper
|
|
339
|
+
*
|
|
340
|
+
* Returns a function that validates URLs and provides defaults.
|
|
341
|
+
*
|
|
342
|
+
* @param defaults - Default values for non-production environments
|
|
343
|
+
* @returns Configuration getter function
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
* ```typescript
|
|
347
|
+
* const getConfig = createProductionConfig({
|
|
348
|
+
* rpcEndpoint: 'http://localhost:8899',
|
|
349
|
+
* apiUrl: 'http://localhost:3000',
|
|
350
|
+
* })
|
|
351
|
+
*
|
|
352
|
+
* // In dev: returns localhost
|
|
353
|
+
* // In prod: throws if env vars not set
|
|
354
|
+
* const rpc = getConfig('rpcEndpoint', process.env.RPC_ENDPOINT)
|
|
355
|
+
* ```
|
|
356
|
+
*/
|
|
357
|
+
export function createProductionConfig<T extends Record<string, string>>(
|
|
358
|
+
defaults: T
|
|
359
|
+
): <K extends keyof T>(key: K, envValue?: string) => string {
|
|
360
|
+
return <K extends keyof T>(key: K, envValue?: string): string => {
|
|
361
|
+
const value = envValue ?? defaults[key]
|
|
362
|
+
|
|
363
|
+
if (isProductionEnvironment() && (!envValue || isLocalhostUrl(value)) && !isLocalhostAllowed()) {
|
|
364
|
+
throw new ProductionSafetyError(
|
|
365
|
+
`Production configuration required for '${String(key)}'. ` +
|
|
366
|
+
`Set the environment variable or ${ALLOW_LOCALHOST_ENV}=true to use defaults.`,
|
|
367
|
+
[{ key: String(key), value: maskSensitiveUrl(value), message: 'Missing production configuration' }]
|
|
368
|
+
)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return value
|
|
372
|
+
}
|
|
373
|
+
}
|