@buoy-gg/env 7.0.40 → 7.0.42
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/README.md +11 -83
- package/lib/commonjs/env/components/EnvStatsOverview.js +1 -159
- package/lib/commonjs/env/components/EnvVarRow.js +1 -189
- package/lib/commonjs/env/components/EnvVarSection.js +1 -84
- package/lib/commonjs/env/components/EnvVarsModal.js +1 -322
- package/lib/commonjs/env/hooks/useDynamicEnv.js +1 -92
- package/lib/commonjs/env/types/index.js +1 -27
- package/lib/commonjs/env/types/types.js +1 -1
- package/lib/commonjs/env/types/userTypes.js +1 -1
- package/lib/commonjs/env/utils/envTypeDetector.js +1 -59
- package/lib/commonjs/env/utils/helpers.js +1 -119
- package/lib/commonjs/env/utils/index.js +1 -38
- package/lib/commonjs/env/utils/remoteEnv.js +1 -24
- package/lib/commonjs/env/utils/utils.js +1 -121
- package/lib/commonjs/index.js +1 -77
- package/lib/commonjs/preset.js +1 -76
- package/lib/commonjs/sync/envSyncAdapter.js +1 -38
- package/lib/module/env/components/EnvStatsOverview.js +1 -155
- package/lib/module/env/components/EnvVarRow.js +1 -185
- package/lib/module/env/components/EnvVarSection.js +1 -80
- package/lib/module/env/components/EnvVarsModal.js +1 -317
- package/lib/module/env/hooks/useDynamicEnv.js +1 -88
- package/lib/module/env/types/index.js +1 -4
- package/lib/module/env/types/types.js +1 -1
- package/lib/module/env/types/userTypes.js +1 -1
- package/lib/module/env/utils/envTypeDetector.js +1 -55
- package/lib/module/env/utils/helpers.js +1 -114
- package/lib/module/env/utils/index.js +1 -5
- package/lib/module/env/utils/remoteEnv.js +1 -19
- package/lib/module/env/utils/utils.js +1 -116
- package/lib/module/index.js +1 -21
- package/lib/module/preset.js +1 -73
- package/lib/module/sync/envSyncAdapter.js +1 -34
- package/lib/typescript/sync/envSyncAdapter.d.ts +5 -7
- package/lib/web/index.mjs +1 -0
- package/package.json +33 -11
- package/lib/typescript/env/components/EnvStatsOverview.d.ts.map +0 -1
- package/lib/typescript/env/components/EnvVarRow.d.ts.map +0 -1
- package/lib/typescript/env/components/EnvVarSection.d.ts.map +0 -1
- package/lib/typescript/env/components/EnvVarsModal.d.ts.map +0 -1
- package/lib/typescript/env/hooks/useDynamicEnv.d.ts.map +0 -1
- package/lib/typescript/env/types/index.d.ts.map +0 -1
- package/lib/typescript/env/types/types.d.ts.map +0 -1
- package/lib/typescript/env/types/userTypes.d.ts.map +0 -1
- package/lib/typescript/env/utils/envTypeDetector.d.ts.map +0 -1
- package/lib/typescript/env/utils/helpers.d.ts.map +0 -1
- package/lib/typescript/env/utils/index.d.ts.map +0 -1
- package/lib/typescript/env/utils/remoteEnv.d.ts.map +0 -1
- package/lib/typescript/env/utils/utils.d.ts.map +0 -1
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/preset.d.ts.map +0 -1
- package/lib/typescript/sync/envSyncAdapter.d.ts.map +0 -1
|
@@ -1,185 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { View, Text, StyleSheet } from "react-native";
|
|
4
|
-
import { CompactRow, TypeBadge, buoyColors, night, monoFont } from "@buoy-gg/shared-ui";
|
|
5
|
-
import { getEnvVarType } from "../utils/envTypeDetector";
|
|
6
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
7
|
-
const getStatusConfig = (status, expectedType) => {
|
|
8
|
-
switch (status) {
|
|
9
|
-
case "required_present":
|
|
10
|
-
return {
|
|
11
|
-
label: "Valid",
|
|
12
|
-
color: buoyColors.success,
|
|
13
|
-
sublabel: "Required"
|
|
14
|
-
};
|
|
15
|
-
case "required_missing":
|
|
16
|
-
return {
|
|
17
|
-
label: "Missing",
|
|
18
|
-
color: buoyColors.error,
|
|
19
|
-
sublabel: "Required"
|
|
20
|
-
};
|
|
21
|
-
case "required_wrong_value":
|
|
22
|
-
return {
|
|
23
|
-
label: "Wrong",
|
|
24
|
-
color: buoyColors.warning,
|
|
25
|
-
sublabel: "Invalid value"
|
|
26
|
-
};
|
|
27
|
-
case "required_wrong_type":
|
|
28
|
-
return {
|
|
29
|
-
label: "Type Error",
|
|
30
|
-
color: buoyColors.info,
|
|
31
|
-
sublabel: "Wrong type"
|
|
32
|
-
};
|
|
33
|
-
case "optional_present":
|
|
34
|
-
return {
|
|
35
|
-
label: "Set",
|
|
36
|
-
color: buoyColors.textSecondary,
|
|
37
|
-
sublabel: "Optional"
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
const formatEnvKey = key => {
|
|
42
|
-
// Format key similar to React Query: "section > subsection"
|
|
43
|
-
return key.split("_").join(" › ");
|
|
44
|
-
};
|
|
45
|
-
const formatValue = value => {
|
|
46
|
-
if (value === undefined || value === null) {
|
|
47
|
-
return "undefined";
|
|
48
|
-
}
|
|
49
|
-
const str = typeof value === "string" ? value : String(value);
|
|
50
|
-
return str;
|
|
51
|
-
};
|
|
52
|
-
export function EnvVarRow({
|
|
53
|
-
envVar,
|
|
54
|
-
isExpanded,
|
|
55
|
-
onPress
|
|
56
|
-
}) {
|
|
57
|
-
const config = getStatusConfig(envVar.status, envVar.expectedType);
|
|
58
|
-
const hasValue = envVar.value !== undefined && envVar.value !== null;
|
|
59
|
-
|
|
60
|
-
// Format primary text like React Query does: "section › subsection"
|
|
61
|
-
// For env vars, we'll show the key formatted nicely
|
|
62
|
-
const keyParts = envVar.key.split("_");
|
|
63
|
-
const primaryText = keyParts.map(part => part.toLowerCase()).join(" › ");
|
|
64
|
-
|
|
65
|
-
// The key is the row's identity everywhere a handle is needed. The visible
|
|
66
|
-
// primary text is the prettified "expo › public › api › url" form, which is
|
|
67
|
-
// no use to a test (or to VoiceOver) trying to find one specific variable.
|
|
68
|
-
const rowID = `buoy-env-row-${envVar.key}`;
|
|
69
|
-
|
|
70
|
-
// Create expanded content for value and expected value
|
|
71
|
-
const expandedContent = /*#__PURE__*/_jsxs(View, {
|
|
72
|
-
style: styles.expandedContainer,
|
|
73
|
-
children: [/*#__PURE__*/_jsxs(View, {
|
|
74
|
-
style: styles.expandedRow,
|
|
75
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
76
|
-
style: styles.expandedLabel,
|
|
77
|
-
children: "Value:"
|
|
78
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
79
|
-
style: styles.expandedValue,
|
|
80
|
-
numberOfLines: 3,
|
|
81
|
-
testID: `${rowID}-value`,
|
|
82
|
-
children: formatValue(envVar.value) || "undefined"
|
|
83
|
-
})]
|
|
84
|
-
}), envVar.status === "required_wrong_type" && envVar.expectedType && /*#__PURE__*/_jsxs(_Fragment, {
|
|
85
|
-
children: [/*#__PURE__*/_jsxs(View, {
|
|
86
|
-
style: styles.expandedRow,
|
|
87
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
88
|
-
style: styles.expandedLabel,
|
|
89
|
-
children: "Type:"
|
|
90
|
-
}), /*#__PURE__*/_jsx(TypeBadge, {
|
|
91
|
-
type: getEnvVarType(envVar.value),
|
|
92
|
-
testID: `${rowID}-actual-type`
|
|
93
|
-
})]
|
|
94
|
-
}), /*#__PURE__*/_jsxs(View, {
|
|
95
|
-
style: styles.expandedRow,
|
|
96
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
97
|
-
style: styles.expandedLabel,
|
|
98
|
-
children: "Expected:"
|
|
99
|
-
}), /*#__PURE__*/_jsx(TypeBadge, {
|
|
100
|
-
type: envVar.expectedType,
|
|
101
|
-
testID: `${rowID}-expected-type`
|
|
102
|
-
})]
|
|
103
|
-
})]
|
|
104
|
-
}), envVar.status === "required_wrong_value" && envVar.expectedValue && /*#__PURE__*/_jsxs(View, {
|
|
105
|
-
style: styles.expandedRow,
|
|
106
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
107
|
-
style: styles.expandedLabel,
|
|
108
|
-
children: "Expected:"
|
|
109
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
110
|
-
style: styles.expandedExpected,
|
|
111
|
-
testID: `${rowID}-expected-value`,
|
|
112
|
-
children: String(envVar.expectedValue)
|
|
113
|
-
})]
|
|
114
|
-
}), envVar.description && /*#__PURE__*/_jsxs(View, {
|
|
115
|
-
style: styles.expandedRow,
|
|
116
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
117
|
-
style: styles.expandedLabel,
|
|
118
|
-
children: "Info:"
|
|
119
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
120
|
-
style: styles.expandedDescription,
|
|
121
|
-
testID: `${rowID}-description`,
|
|
122
|
-
children: envVar.description
|
|
123
|
-
})]
|
|
124
|
-
})]
|
|
125
|
-
});
|
|
126
|
-
return /*#__PURE__*/_jsx(CompactRow, {
|
|
127
|
-
statusDotColor: config.color,
|
|
128
|
-
statusLabel: config.label,
|
|
129
|
-
statusSublabel: config.sublabel,
|
|
130
|
-
primaryText: primaryText,
|
|
131
|
-
secondaryText: undefined // Don't show value inline anymore
|
|
132
|
-
,
|
|
133
|
-
expandedContent: expandedContent,
|
|
134
|
-
isExpanded: isExpanded,
|
|
135
|
-
expandedGlowColor: config.color,
|
|
136
|
-
customBadge: envVar.expectedType ? /*#__PURE__*/_jsx(TypeBadge, {
|
|
137
|
-
type: envVar.expectedType,
|
|
138
|
-
testID: `${rowID}-type-badge`
|
|
139
|
-
}) : undefined,
|
|
140
|
-
showChevron: true,
|
|
141
|
-
onPress: onPress ? () => onPress(envVar) : undefined,
|
|
142
|
-
testID: rowID,
|
|
143
|
-
statusTestID: `${rowID}-status`,
|
|
144
|
-
statusSublabelTestID: `${rowID}-category`
|
|
145
|
-
// The status is carried by a coloured dot and a two-word label that the
|
|
146
|
-
// row's container merges away on Fabric — spelled out here so the verdict
|
|
147
|
-
// reaches assistive tech (and Maestro) with the key it belongs to.
|
|
148
|
-
,
|
|
149
|
-
accessibilityLabel: `${envVar.key}, ${config.label}, ${config.sublabel}`
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
const styles = StyleSheet.create({
|
|
153
|
-
expandedContainer: {
|
|
154
|
-
gap: 6
|
|
155
|
-
},
|
|
156
|
-
expandedRow: {
|
|
157
|
-
flexDirection: "row",
|
|
158
|
-
alignItems: "center",
|
|
159
|
-
gap: 8
|
|
160
|
-
},
|
|
161
|
-
expandedLabel: {
|
|
162
|
-
fontSize: 10,
|
|
163
|
-
color: night.color.textTertiary,
|
|
164
|
-
fontWeight: "600",
|
|
165
|
-
minWidth: 60,
|
|
166
|
-
fontFamily: monoFont
|
|
167
|
-
},
|
|
168
|
-
expandedValue: {
|
|
169
|
-
fontSize: 11,
|
|
170
|
-
color: night.color.textSecondary,
|
|
171
|
-
fontFamily: monoFont,
|
|
172
|
-
flex: 1
|
|
173
|
-
},
|
|
174
|
-
expandedExpected: {
|
|
175
|
-
fontSize: 11,
|
|
176
|
-
color: buoyColors.warning,
|
|
177
|
-
fontFamily: monoFont,
|
|
178
|
-
flex: 1
|
|
179
|
-
},
|
|
180
|
-
expandedDescription: {
|
|
181
|
-
fontSize: 11,
|
|
182
|
-
color: night.color.textSecondary,
|
|
183
|
-
flex: 1
|
|
184
|
-
}
|
|
185
|
-
});
|
|
1
|
+
"use strict";import{View as s,Text as l,StyleSheet as m}from"react-native";import{CompactRow as h,TypeBadge as c,buoyColors as a,night as p,monoFont as u}from"@buoy-gg/shared-ui";import{getEnvVarType as w}from"../utils/envTypeDetector";import{jsx as n,jsxs as d,Fragment as T}from"react/jsx-runtime";const _=(e,i)=>{switch(e){case"required_present":return{label:"Valid",color:a.success,sublabel:"Required"};case"required_missing":return{label:"Missing",color:a.error,sublabel:"Required"};case"required_wrong_value":return{label:"Wrong",color:a.warning,sublabel:"Invalid value"};case"required_wrong_type":return{label:"Type Error",color:a.info,sublabel:"Wrong type"};case"optional_present":return{label:"Set",color:a.textSecondary,sublabel:"Optional"}}};const R=e=>{return e.split("_").join(" \u203A ")};const S=e=>{if(e===void 0||e===null){return"undefined"}const i=typeof e==="string"?e:String(e);return i};export function EnvVarRow({envVar:e,isExpanded:i,onPress:x}){const r=_(e.status,e.expectedType);const D=e.value!==void 0&&e.value!==null;const y=e.key.split("_");const b=y.map(g=>g.toLowerCase()).join(" \u203A ");const o=`buoy-env-row-${e.key}`;const f=d(s,{style:t.expandedContainer,children:[d(s,{style:t.expandedRow,children:[n(l,{style:t.expandedLabel,children:"Value:"}),n(l,{style:t.expandedValue,numberOfLines:3,testID:`${o}-value`,children:S(e.value)||"undefined"})]}),e.status==="required_wrong_type"&&e.expectedType&&d(T,{children:[d(s,{style:t.expandedRow,children:[n(l,{style:t.expandedLabel,children:"Type:"}),n(c,{type:w(e.value),testID:`${o}-actual-type`})]}),d(s,{style:t.expandedRow,children:[n(l,{style:t.expandedLabel,children:"Expected:"}),n(c,{type:e.expectedType,testID:`${o}-expected-type`})]})]}),e.status==="required_wrong_value"&&e.expectedValue&&d(s,{style:t.expandedRow,children:[n(l,{style:t.expandedLabel,children:"Expected:"}),n(l,{style:t.expandedExpected,testID:`${o}-expected-value`,children:String(e.expectedValue)})]}),e.description&&d(s,{style:t.expandedRow,children:[n(l,{style:t.expandedLabel,children:"Info:"}),n(l,{style:t.expandedDescription,testID:`${o}-description`,children:e.description})]})]});return n(h,{statusDotColor:r.color,statusLabel:r.label,statusSublabel:r.sublabel,primaryText:b,secondaryText:void 0,expandedContent:f,isExpanded:i,expandedGlowColor:r.color,customBadge:e.expectedType?n(c,{type:e.expectedType,testID:`${o}-type-badge`}):void 0,showChevron:true,onPress:x?()=>x(e):void 0,testID:o,statusTestID:`${o}-status`,statusSublabelTestID:`${o}-category`,accessibilityLabel:`${e.key}, ${r.label}, ${r.sublabel}`})}const t=m.create({expandedContainer:{gap:6},expandedRow:{flexDirection:"row",alignItems:"center",gap:8},expandedLabel:{fontSize:10,color:p.color.textTertiary,fontWeight:"600",minWidth:60,fontFamily:u},expandedValue:{fontSize:11,color:p.color.textSecondary,fontFamily:u,flex:1},expandedExpected:{fontSize:11,color:a.warning,fontFamily:u,flex:1},expandedDescription:{fontSize:11,color:p.color.textSecondary,flex:1}});
|
|
@@ -1,80 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { useCallback, useState } from "react";
|
|
4
|
-
import { View, Text, StyleSheet } from "react-native";
|
|
5
|
-
import { EnvVarRow } from "./EnvVarRow";
|
|
6
|
-
import { SectionHeader, night, monoFont } from "@buoy-gg/shared-ui";
|
|
7
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
|
-
export function EnvVarSection({
|
|
9
|
-
title,
|
|
10
|
-
count,
|
|
11
|
-
vars,
|
|
12
|
-
emptyMessage
|
|
13
|
-
}) {
|
|
14
|
-
const [expandedVar, setExpandedVar] = useState(null);
|
|
15
|
-
const handleVarPress = useCallback(envVar => {
|
|
16
|
-
setExpandedVar(prev => prev === envVar.key ? null : envVar.key);
|
|
17
|
-
}, []);
|
|
18
|
-
if (vars.length === 0 && title === "Required Variables") {
|
|
19
|
-
return /*#__PURE__*/_jsxs(View, {
|
|
20
|
-
style: styles.sectionContainer,
|
|
21
|
-
children: [/*#__PURE__*/_jsxs(SectionHeader, {
|
|
22
|
-
children: [/*#__PURE__*/_jsx(SectionHeader.Title, {
|
|
23
|
-
children: title
|
|
24
|
-
}), /*#__PURE__*/_jsx(SectionHeader.Badge, {
|
|
25
|
-
count: 0,
|
|
26
|
-
color: night.color.accent
|
|
27
|
-
})]
|
|
28
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
29
|
-
style: styles.emptySection,
|
|
30
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
31
|
-
style: styles.emptySectionText,
|
|
32
|
-
children: emptyMessage
|
|
33
|
-
})
|
|
34
|
-
})]
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
if (vars.length === 0) return null;
|
|
38
|
-
return /*#__PURE__*/_jsxs(View, {
|
|
39
|
-
style: styles.sectionContainer,
|
|
40
|
-
children: [title !== "" && /*#__PURE__*/_jsxs(SectionHeader, {
|
|
41
|
-
children: [/*#__PURE__*/_jsx(SectionHeader.Title, {
|
|
42
|
-
children: title
|
|
43
|
-
}), /*#__PURE__*/_jsx(SectionHeader.Badge, {
|
|
44
|
-
count: count,
|
|
45
|
-
color: night.color.accent
|
|
46
|
-
})]
|
|
47
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
48
|
-
style: styles.sectionContent,
|
|
49
|
-
children: vars.map(envVar => /*#__PURE__*/_jsx(EnvVarRow, {
|
|
50
|
-
envVar: envVar,
|
|
51
|
-
isExpanded: expandedVar === envVar.key,
|
|
52
|
-
onPress: handleVarPress
|
|
53
|
-
}, envVar.key))
|
|
54
|
-
})]
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
const styles = StyleSheet.create({
|
|
58
|
-
sectionContainer: {
|
|
59
|
-
gap: 8
|
|
60
|
-
},
|
|
61
|
-
sectionContent: {
|
|
62
|
-
// No gap needed, EnvVarRow has its own margins
|
|
63
|
-
},
|
|
64
|
-
emptySection: {
|
|
65
|
-
padding: 20,
|
|
66
|
-
backgroundColor: night.color.accent + "08",
|
|
67
|
-
borderRadius: 8,
|
|
68
|
-
borderWidth: 1,
|
|
69
|
-
borderColor: night.color.accent + "20",
|
|
70
|
-
alignItems: "center"
|
|
71
|
-
},
|
|
72
|
-
emptySectionText: {
|
|
73
|
-
color: night.color.accent,
|
|
74
|
-
fontSize: 11,
|
|
75
|
-
textAlign: "center",
|
|
76
|
-
fontFamily: monoFont,
|
|
77
|
-
opacity: 0.8,
|
|
78
|
-
letterSpacing: 0.5
|
|
79
|
-
}
|
|
80
|
-
});
|
|
1
|
+
"use strict";import{useCallback as u,useState as x}from"react";import{View as i,Text as g,StyleSheet as S}from"react-native";import{EnvVarRow as f}from"./EnvVarRow";import{SectionHeader as n,night as o,monoFont as C}from"@buoy-gg/shared-ui";import{jsx as e,jsxs as l}from"react/jsx-runtime";export function EnvVarSection({title:c,count:a,vars:s,emptyMessage:d}){const[m,p]=x(null);const y=u(t=>{p(h=>h===t.key?null:t.key)},[]);if(s.length===0&&c==="Required Variables"){return l(i,{style:r.sectionContainer,children:[l(n,{children:[e(n.Title,{children:c}),e(n.Badge,{count:0,color:o.color.accent})]}),e(i,{style:r.emptySection,children:e(g,{style:r.emptySectionText,children:d})})]})}if(s.length===0)return null;return l(i,{style:r.sectionContainer,children:[c!==""&&l(n,{children:[e(n.Title,{children:c}),e(n.Badge,{count:a,color:o.color.accent})]}),e(i,{style:r.sectionContent,children:s.map(t=>e(f,{envVar:t,isExpanded:m===t.key,onPress:y},t.key))})]})}const r=S.create({sectionContainer:{gap:8},sectionContent:{},emptySection:{padding:20,backgroundColor:o.color.accent+"08",borderRadius:8,borderWidth:1,borderColor:o.color.accent+"20",alignItems:"center"},emptySectionText:{color:o.color.accent,fontSize:11,textAlign:"center",fontFamily:C,opacity:.8,letterSpacing:.5}});
|
|
@@ -1,317 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { JsModal, devToolsStorageKeys, ModalHeader, HeaderSearchButton, displayValue, buoyColors, night, windowDragProps, ToolEmptyState, resolveListEmptyState, monoFont } from "@buoy-gg/shared-ui";
|
|
4
|
-
import { useCallback, useState, useRef, useEffect, useMemo } from "react";
|
|
5
|
-
import { View, StyleSheet, ScrollView, Text } from "react-native";
|
|
6
|
-
import { EnvStatsOverview } from "./EnvStatsOverview";
|
|
7
|
-
import { useDynamicEnv } from "../hooks/useDynamicEnv";
|
|
8
|
-
import { processEnvVars, calculateStats } from "../utils";
|
|
9
|
-
import { EnvVarSection } from "./EnvVarSection";
|
|
10
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
-
/**
|
|
12
|
-
* Full-screen modal for inspecting required environment variables. Handles automatic
|
|
13
|
-
* discovery, validation, filtering, and rich search so teams can quickly spot missing
|
|
14
|
-
* or misconfigured values during development sessions.
|
|
15
|
-
*/
|
|
16
|
-
export function EnvVarsModal({
|
|
17
|
-
visible,
|
|
18
|
-
onClose,
|
|
19
|
-
requiredEnvVars,
|
|
20
|
-
onBack,
|
|
21
|
-
onMinimize,
|
|
22
|
-
enableSharedModalDimensions = false,
|
|
23
|
-
inline = false
|
|
24
|
-
}) {
|
|
25
|
-
const [activeFilter, setActiveFilter] = useState("all");
|
|
26
|
-
const [isSearchActive, setIsSearchActive] = useState(false);
|
|
27
|
-
const [searchQuery, setSearchQuery] = useState("");
|
|
28
|
-
const searchInputRef = useRef(null);
|
|
29
|
-
const handleModeChange = useCallback(_mode => {
|
|
30
|
-
// Mode changes handled by JsModal
|
|
31
|
-
}, []);
|
|
32
|
-
|
|
33
|
-
// Focus search input when search becomes active
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
if (isSearchActive && searchInputRef.current) {
|
|
36
|
-
searchInputRef.current.focus();
|
|
37
|
-
}
|
|
38
|
-
}, [isSearchActive]);
|
|
39
|
-
|
|
40
|
-
// Clear search when changing filters
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
setSearchQuery("");
|
|
43
|
-
setIsSearchActive(false);
|
|
44
|
-
}, [activeFilter]);
|
|
45
|
-
|
|
46
|
-
// Auto-collect environment variables
|
|
47
|
-
const envResults = useDynamicEnv();
|
|
48
|
-
const autoCollectedEnvVars = useMemo(() => {
|
|
49
|
-
const envVars = {};
|
|
50
|
-
envResults.forEach(({
|
|
51
|
-
key,
|
|
52
|
-
data
|
|
53
|
-
}) => {
|
|
54
|
-
if (data !== undefined && data !== null) {
|
|
55
|
-
envVars[key] = typeof data === "string" ? data : displayValue(data);
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
return envVars;
|
|
59
|
-
}, [envResults]);
|
|
60
|
-
|
|
61
|
-
// Process and categorize environment variables
|
|
62
|
-
const {
|
|
63
|
-
requiredVars,
|
|
64
|
-
optionalVars
|
|
65
|
-
} = useMemo(() => {
|
|
66
|
-
return processEnvVars(autoCollectedEnvVars, requiredEnvVars);
|
|
67
|
-
}, [autoCollectedEnvVars, requiredEnvVars]);
|
|
68
|
-
|
|
69
|
-
// Calculate statistics
|
|
70
|
-
const stats = useMemo(() => {
|
|
71
|
-
if (requiredEnvVars === undefined) {
|
|
72
|
-
return {
|
|
73
|
-
totalCount: 0,
|
|
74
|
-
requiredCount: 0,
|
|
75
|
-
optionalCount: 0,
|
|
76
|
-
presentRequiredCount: 0,
|
|
77
|
-
missingCount: 0,
|
|
78
|
-
wrongValueCount: 0,
|
|
79
|
-
wrongTypeCount: 0
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
return calculateStats(requiredVars, optionalVars, autoCollectedEnvVars);
|
|
83
|
-
}, [requiredEnvVars, requiredVars, optionalVars, autoCollectedEnvVars]);
|
|
84
|
-
|
|
85
|
-
// Combine all vars and sort by priority (issues first)
|
|
86
|
-
const allVars = useMemo(() => {
|
|
87
|
-
const combined = [...requiredVars, ...optionalVars];
|
|
88
|
-
|
|
89
|
-
// Sort by status priority: errors first, then warnings, then valid
|
|
90
|
-
return combined.sort((a, b) => {
|
|
91
|
-
const priorityMap = {
|
|
92
|
-
"required_missing": 1,
|
|
93
|
-
"required_wrong_type": 2,
|
|
94
|
-
"required_wrong_value": 3,
|
|
95
|
-
"required_present": 4,
|
|
96
|
-
"optional_present": 5
|
|
97
|
-
};
|
|
98
|
-
return (priorityMap[a.status] || 999) - (priorityMap[b.status] || 999);
|
|
99
|
-
});
|
|
100
|
-
}, [requiredVars, optionalVars]);
|
|
101
|
-
|
|
102
|
-
// Filter variables based on active filter and search
|
|
103
|
-
const filteredVars = useMemo(() => {
|
|
104
|
-
let vars = [];
|
|
105
|
-
switch (activeFilter) {
|
|
106
|
-
case "all":
|
|
107
|
-
vars = allVars;
|
|
108
|
-
break;
|
|
109
|
-
case "missing":
|
|
110
|
-
vars = allVars.filter(v => v.status === "required_missing");
|
|
111
|
-
break;
|
|
112
|
-
case "issues":
|
|
113
|
-
vars = allVars.filter(v => v.status === "required_wrong_type" || v.status === "required_wrong_value");
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Apply search filter
|
|
118
|
-
if (searchQuery) {
|
|
119
|
-
const query = searchQuery.toLowerCase();
|
|
120
|
-
vars = vars.filter(v => v.key.toLowerCase().includes(query) || v.description?.toLowerCase().includes(query) || typeof v.value === 'string' && v.value.toLowerCase().includes(query));
|
|
121
|
-
}
|
|
122
|
-
return vars;
|
|
123
|
-
}, [allVars, optionalVars, activeFilter, searchQuery]);
|
|
124
|
-
|
|
125
|
-
// Calculate health percentage
|
|
126
|
-
const healthPercentage = stats.requiredCount > 0 ? Math.round(stats.presentRequiredCount / stats.requiredCount * 100) : 100;
|
|
127
|
-
const healthStatus = healthPercentage === 100 ? "HEALTHY" : healthPercentage >= 75 ? "WARNING" : healthPercentage >= 50 ? "ERROR" : "CRITICAL";
|
|
128
|
-
const healthColor = healthPercentage === 100 ? buoyColors.success : healthPercentage >= 75 ? buoyColors.warning : healthPercentage >= 50 ? buoyColors.error : buoyColors.error;
|
|
129
|
-
if (!visible) return null;
|
|
130
|
-
const storagePrefix = enableSharedModalDimensions ? devToolsStorageKeys.modal.root() : devToolsStorageKeys.env.modal();
|
|
131
|
-
const header = /*#__PURE__*/_jsxs(ModalHeader, {
|
|
132
|
-
children: [onBack && /*#__PURE__*/_jsx(ModalHeader.Navigation, {
|
|
133
|
-
onBack: onBack
|
|
134
|
-
}), /*#__PURE__*/_jsx(ModalHeader.Content, {
|
|
135
|
-
title: isSearchActive ? "" : "Environment Variables",
|
|
136
|
-
noMargin: isSearchActive,
|
|
137
|
-
children: isSearchActive && /*#__PURE__*/_jsx(ModalHeader.Search, {
|
|
138
|
-
value: searchQuery,
|
|
139
|
-
onChangeText: setSearchQuery,
|
|
140
|
-
onCancel: () => {
|
|
141
|
-
setIsSearchActive(false);
|
|
142
|
-
setSearchQuery("");
|
|
143
|
-
},
|
|
144
|
-
placeholder: "Search env keys...",
|
|
145
|
-
accessibilityLabel: "Search env keys",
|
|
146
|
-
testID: "buoy-env-search"
|
|
147
|
-
// The field itself is a handle Detox can use but XCUITest
|
|
148
|
-
// cannot: on this RN version a TextInput's testID never
|
|
149
|
-
// reaches the XCUITest element tree — iOS folds the input
|
|
150
|
-
// into the header's accessibility container. This wrapper
|
|
151
|
-
// handle is the one the a11y lane can actually see.
|
|
152
|
-
,
|
|
153
|
-
containerTestID: "buoy-env-search-field",
|
|
154
|
-
clearTestID: "buoy-env-search-clear"
|
|
155
|
-
})
|
|
156
|
-
}), /*#__PURE__*/_jsx(ModalHeader.Actions, {
|
|
157
|
-
children: !isSearchActive && /*#__PURE__*/_jsx(HeaderSearchButton, {
|
|
158
|
-
onPress: () => setIsSearchActive(true),
|
|
159
|
-
testID: "buoy-env-search-open",
|
|
160
|
-
accessibilityLabel: "Search env keys"
|
|
161
|
-
})
|
|
162
|
-
})]
|
|
163
|
-
});
|
|
164
|
-
const body = /*#__PURE__*/_jsxs(ScrollView, {
|
|
165
|
-
style: styles.scrollContainer,
|
|
166
|
-
contentContainerStyle: styles.contentContainer,
|
|
167
|
-
showsVerticalScrollIndicator: false,
|
|
168
|
-
testID: "buoy-env-scroll",
|
|
169
|
-
children: [/*#__PURE__*/_jsx(EnvStatsOverview, {
|
|
170
|
-
stats: stats,
|
|
171
|
-
healthPercentage: healthPercentage,
|
|
172
|
-
healthStatus: healthStatus,
|
|
173
|
-
healthColor: healthColor,
|
|
174
|
-
activeFilter: activeFilter,
|
|
175
|
-
onFilterChange: setActiveFilter
|
|
176
|
-
}), filteredVars.length > 0 ? /*#__PURE__*/_jsxs(View, {
|
|
177
|
-
style: styles.varsSection,
|
|
178
|
-
children: [/*#__PURE__*/_jsxs(View, {
|
|
179
|
-
style: styles.sectionHeader,
|
|
180
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
181
|
-
style: styles.sectionTitle,
|
|
182
|
-
testID: "buoy-env-section-title",
|
|
183
|
-
children: activeFilter === "all" ? "ALL VARIABLES" : activeFilter === "missing" ? "MISSING VARIABLES" : "ISSUES TO FIX"
|
|
184
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
185
|
-
style: styles.countBadge,
|
|
186
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
187
|
-
style: styles.countText,
|
|
188
|
-
testID: "buoy-env-section-count",
|
|
189
|
-
children: filteredVars.length
|
|
190
|
-
})
|
|
191
|
-
})]
|
|
192
|
-
}), /*#__PURE__*/_jsx(EnvVarSection, {
|
|
193
|
-
title: "",
|
|
194
|
-
count: 0,
|
|
195
|
-
vars: filteredVars,
|
|
196
|
-
emptyMessage: ""
|
|
197
|
-
})]
|
|
198
|
-
}) : /*#__PURE__*/_jsx(ToolEmptyState, {
|
|
199
|
-
testID: "buoy-env-empty",
|
|
200
|
-
...resolveListEmptyState({
|
|
201
|
-
subject: "variables",
|
|
202
|
-
verb: "declared",
|
|
203
|
-
search: searchQuery,
|
|
204
|
-
total: allVars.length,
|
|
205
|
-
waiting: {
|
|
206
|
-
// A missing .env is a config problem, not a stream that has
|
|
207
|
-
// not started — `keys` is the env-and-secrets state.
|
|
208
|
-
otto: "keys",
|
|
209
|
-
title: "No variables",
|
|
210
|
-
message: `No ${activeFilter === "all" ? "" : activeFilter} variables found`
|
|
211
|
-
}
|
|
212
|
-
})
|
|
213
|
-
})]
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
// The ONLY difference between the phone and the desktop dashboard is which
|
|
217
|
-
// shell the header and body go in. Everything above this line is shared.
|
|
218
|
-
if (inline) {
|
|
219
|
-
return /*#__PURE__*/_jsxs(View, {
|
|
220
|
-
style: styles.inlineRoot,
|
|
221
|
-
children: [/*#__PURE__*/_jsx(View, {
|
|
222
|
-
style: styles.inlineHeader,
|
|
223
|
-
...windowDragProps,
|
|
224
|
-
children: header
|
|
225
|
-
}), body]
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
return /*#__PURE__*/_jsx(JsModal, {
|
|
229
|
-
visible: visible,
|
|
230
|
-
onClose: onClose,
|
|
231
|
-
onMinimize: onMinimize,
|
|
232
|
-
persistenceKey: storagePrefix,
|
|
233
|
-
header: {
|
|
234
|
-
customContent: header,
|
|
235
|
-
showToggleButton: true
|
|
236
|
-
},
|
|
237
|
-
onModeChange: handleModeChange,
|
|
238
|
-
enablePersistence: true,
|
|
239
|
-
initialMode: "bottomSheet",
|
|
240
|
-
enableGlitchEffects: true,
|
|
241
|
-
styles: {},
|
|
242
|
-
children: body
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* The same env tool as `EnvVarsModal`, rendered INLINE for hosts that place
|
|
248
|
-
* tools in a panel. Exists so there is exactly one implementation — the desktop
|
|
249
|
-
* dashboard used to ship its own copy of this component's internals.
|
|
250
|
-
*/
|
|
251
|
-
export function EnvToolView({
|
|
252
|
-
requiredEnvVars
|
|
253
|
-
}) {
|
|
254
|
-
return /*#__PURE__*/_jsx(EnvVarsModal, {
|
|
255
|
-
inline: true,
|
|
256
|
-
visible: true,
|
|
257
|
-
requiredEnvVars: requiredEnvVars,
|
|
258
|
-
onClose: () => {}
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
const styles = StyleSheet.create({
|
|
262
|
-
inlineRoot: {
|
|
263
|
-
flex: 1,
|
|
264
|
-
minHeight: 0,
|
|
265
|
-
// Transparent so the desktop workbench's hoisted sky (one field spanning
|
|
266
|
-
// sidebar + panel) shows through — the shell paints its base beneath it.
|
|
267
|
-
backgroundColor: "transparent"
|
|
268
|
-
},
|
|
269
|
-
inlineHeader: {
|
|
270
|
-
paddingHorizontal: 12,
|
|
271
|
-
paddingVertical: 8,
|
|
272
|
-
borderBottomWidth: 1,
|
|
273
|
-
borderBottomColor: buoyColors.border,
|
|
274
|
-
// Clear, like Bench — the host's hoisted sky shows through the header.
|
|
275
|
-
backgroundColor: "transparent"
|
|
276
|
-
},
|
|
277
|
-
scrollContainer: {
|
|
278
|
-
flex: 1,
|
|
279
|
-
// Transparent so JsModal's ToolBackground shows through (the modal paints opaque base beneath it).
|
|
280
|
-
backgroundColor: "transparent"
|
|
281
|
-
},
|
|
282
|
-
contentContainer: {
|
|
283
|
-
padding: 8,
|
|
284
|
-
paddingBottom: 24
|
|
285
|
-
},
|
|
286
|
-
varsSection: {
|
|
287
|
-
marginTop: 16
|
|
288
|
-
},
|
|
289
|
-
sectionHeader: {
|
|
290
|
-
flexDirection: "row",
|
|
291
|
-
justifyContent: "space-between",
|
|
292
|
-
alignItems: "center",
|
|
293
|
-
marginBottom: 8,
|
|
294
|
-
paddingHorizontal: 8
|
|
295
|
-
},
|
|
296
|
-
sectionTitle: {
|
|
297
|
-
fontSize: 10,
|
|
298
|
-
fontWeight: "700",
|
|
299
|
-
color: night.color.textTertiary,
|
|
300
|
-
letterSpacing: 1.2,
|
|
301
|
-
fontFamily: monoFont
|
|
302
|
-
},
|
|
303
|
-
countBadge: {
|
|
304
|
-
backgroundColor: night.color.accent + "15",
|
|
305
|
-
paddingHorizontal: 8,
|
|
306
|
-
paddingVertical: 2,
|
|
307
|
-
borderRadius: 9999,
|
|
308
|
-
borderWidth: 1,
|
|
309
|
-
borderColor: night.color.accent + "40"
|
|
310
|
-
},
|
|
311
|
-
countText: {
|
|
312
|
-
fontSize: 10,
|
|
313
|
-
fontWeight: "500",
|
|
314
|
-
color: night.color.accent,
|
|
315
|
-
fontFamily: monoFont
|
|
316
|
-
}
|
|
317
|
-
});
|
|
1
|
+
"use strict";import{JsModal as O,devToolsStorageKeys as M,ModalHeader as d,HeaderSearchButton as K,displayValue as Q,buoyColors as h,night as S,windowDragProps as J,ToolEmptyState as U,resolveListEmptyState as X,monoFont as q}from"@buoy-gg/shared-ui";import{useCallback as Y,useState as V,useRef as $,useEffect as A,useMemo as g}from"react";import{View as f,StyleSheet as Z,ScrollView as ee,Text as H}from"react-native";import{EnvStatsOverview as te}from"./EnvStatsOverview";import{useDynamicEnv as oe}from"../hooks/useDynamicEnv";import{processEnvVars as re,calculateStats as ne}from"../utils";import{EnvVarSection as se}from"./EnvVarSection";import{jsx as e,jsxs as y}from"react/jsx-runtime";export function EnvVarsModal({visible:p,onClose:D,requiredEnvVars:m,onBack:E,onMinimize:B,enableSharedModalDimensions:F=false,inline:j=false}){const[s,z]=V("all");const[a,w]=V(false);const[l,I]=V("");const x=$(null);const N=Y(t=>{},[]);A(()=>{if(a&&x.current){x.current.focus()}},[a]);A(()=>{I("");w(false)},[s]);const R=oe();const C=g(()=>{const t={};R.forEach(({key:o,data:r})=>{if(r!==void 0&&r!==null){t[o]=typeof r==="string"?r:Q(r)}});return t},[R]);const{requiredVars:b,optionalVars:c}=g(()=>{return re(C,m)},[C,m]);const v=g(()=>{if(m===void 0){return{totalCount:0,requiredCount:0,optionalCount:0,presentRequiredCount:0,missingCount:0,wrongValueCount:0,wrongTypeCount:0}}return ne(b,c,C)},[m,b,c,C]);const u=g(()=>{const t=[...b,...c];return t.sort((o,r)=>{const L={"required_missing":1,"required_wrong_type":2,"required_wrong_value":3,"required_present":4,"optional_present":5};return(L[o.status]||999)-(L[r.status]||999)})},[b,c]);const T=g(()=>{let t=[];switch(s){case"all":t=u;break;case"missing":t=u.filter(o=>o.status==="required_missing");break;case"issues":t=u.filter(o=>o.status==="required_wrong_type"||o.status==="required_wrong_value");break}if(l){const o=l.toLowerCase();t=t.filter(r=>r.key.toLowerCase().includes(o)||r.description?.toLowerCase().includes(o)||typeof r.value==="string"&&r.value.toLowerCase().includes(o))}return t},[u,c,s,l]);const i=v.requiredCount>0?Math.round(v.presentRequiredCount/v.requiredCount*100):100;const P=i===100?"HEALTHY":i>=75?"WARNING":i>=50?"ERROR":"CRITICAL";const W=i===100?h.success:i>=75?h.warning:i>=50?h.error:h.error;if(!p)return null;const G=F?M.modal.root():M.env.modal();const _=y(d,{children:[E&&e(d.Navigation,{onBack:E}),e(d.Content,{title:a?"":"Environment Variables",noMargin:a,children:a&&e(d.Search,{value:l,onChangeText:I,onCancel:()=>{w(false);I("")},placeholder:"Search env keys...",accessibilityLabel:"Search env keys",testID:"buoy-env-search",containerTestID:"buoy-env-search-field",clearTestID:"buoy-env-search-clear"})}),e(d.Actions,{children:!a&&e(K,{onPress:()=>w(true),testID:"buoy-env-search-open",accessibilityLabel:"Search env keys"})})]});const k=y(ee,{style:n.scrollContainer,contentContainerStyle:n.contentContainer,showsVerticalScrollIndicator:false,testID:"buoy-env-scroll",children:[e(te,{stats:v,healthPercentage:i,healthStatus:P,healthColor:W,activeFilter:s,onFilterChange:z}),T.length>0?y(f,{style:n.varsSection,children:[y(f,{style:n.sectionHeader,children:[e(H,{style:n.sectionTitle,testID:"buoy-env-section-title",children:s==="all"?"ALL VARIABLES":s==="missing"?"MISSING VARIABLES":"ISSUES TO FIX"}),e(f,{style:n.countBadge,children:e(H,{style:n.countText,testID:"buoy-env-section-count",children:T.length})})]}),e(se,{title:"",count:0,vars:T,emptyMessage:""})]}):e(U,{testID:"buoy-env-empty",...X({subject:"variables",verb:"declared",search:l,total:u.length,waiting:{otto:"keys",title:"No variables",message:`No ${s==="all"?"":s} variables found`}})})]});if(j){return y(f,{style:n.inlineRoot,children:[e(f,{style:n.inlineHeader,...J,children:_}),k]})}return e(O,{visible:p,onClose:D,onMinimize:B,persistenceKey:G,header:{customContent:_,showToggleButton:true},onModeChange:N,enablePersistence:true,initialMode:"bottomSheet",enableGlitchEffects:true,styles:{},children:k})}export function EnvToolView({requiredEnvVars:p}){return e(EnvVarsModal,{inline:true,visible:true,requiredEnvVars:p,onClose:()=>{}})}const n=Z.create({inlineRoot:{flex:1,minHeight:0,backgroundColor:"transparent"},inlineHeader:{paddingHorizontal:12,paddingVertical:8,borderBottomWidth:1,borderBottomColor:h.border,backgroundColor:"transparent"},scrollContainer:{flex:1,backgroundColor:"transparent"},contentContainer:{padding:8,paddingBottom:24},varsSection:{marginTop:16},sectionHeader:{flexDirection:"row",justifyContent:"space-between",alignItems:"center",marginBottom:8,paddingHorizontal:8},sectionTitle:{fontSize:10,fontWeight:"700",color:S.color.textTertiary,letterSpacing:1.2,fontFamily:q},countBadge:{backgroundColor:S.color.accent+"15",paddingHorizontal:8,paddingVertical:2,borderRadius:9999,borderWidth:1,borderColor:S.color.accent+"40"},countText:{fontSize:10,fontWeight:"500",color:S.color.accent,fontFamily:q}});
|
|
@@ -1,88 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { useMemo } from "react";
|
|
4
|
-
import { getRemoteEnv } from "../utils/remoteEnv";
|
|
5
|
-
/**
|
|
6
|
-
* Hook that returns all available environment variables with parsed values
|
|
7
|
-
* Includes all available environment variables by default (only EXPO_PUBLIC_ prefixed vars are loaded by Expo)
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* // Get all available environment variables (only EXPO_PUBLIC_ prefixed)
|
|
11
|
-
* const envVars = useDynamicEnv();
|
|
12
|
-
* // Returns: [
|
|
13
|
-
* // { key: 'EXPO_PUBLIC_API_URL', data: 'https://api.example.com' },
|
|
14
|
-
* // { key: 'EXPO_PUBLIC_APP_NAME', data: 'MyApp' },
|
|
15
|
-
* // ...
|
|
16
|
-
* // ]
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* // Filter to specific variables
|
|
20
|
-
* const envVars = useDynamicEnv({
|
|
21
|
-
* envFilter: (key) => key.includes('API') || key.includes('URL')
|
|
22
|
-
* });
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* // Filter by value content
|
|
26
|
-
* const envVars = useDynamicEnv({
|
|
27
|
-
* envFilter: (key, value) => value !== undefined && value.length > 0
|
|
28
|
-
* });
|
|
29
|
-
*/
|
|
30
|
-
export function useDynamicEnv({
|
|
31
|
-
envFilter = () => true // Default: include all available environment variables (EXPO_PUBLIC_ only)
|
|
32
|
-
} = {}) {
|
|
33
|
-
// In remote mirror mode (desktop dashboard) env values come from the
|
|
34
|
-
// synced device instead of the local process.env
|
|
35
|
-
const envSource =
|
|
36
|
-
// @ts-ignore process.env does exist
|
|
37
|
-
getRemoteEnv() ?? process.env;
|
|
38
|
-
|
|
39
|
-
// Helper function to get a single environment variable value
|
|
40
|
-
const getEnvValue = useMemo(() => {
|
|
41
|
-
return key => {
|
|
42
|
-
const value = envSource[key];
|
|
43
|
-
if (value === undefined) {
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Try to parse as JSON for complex values, fall back to string
|
|
48
|
-
try {
|
|
49
|
-
// Only attempt JSON parsing if it looks like JSON (starts with { or [)
|
|
50
|
-
if (value.startsWith("{") || value.startsWith("[")) {
|
|
51
|
-
return JSON.parse(value);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Parse boolean-like strings
|
|
55
|
-
if (value.toLowerCase() === "true") return true;
|
|
56
|
-
if (value.toLowerCase() === "false") return false;
|
|
57
|
-
|
|
58
|
-
// Parse number-like strings
|
|
59
|
-
if (/^\d+$/.test(value)) {
|
|
60
|
-
const num = parseInt(value, 10);
|
|
61
|
-
return !isNaN(num) ? num : value;
|
|
62
|
-
}
|
|
63
|
-
if (/^\d*\.\d+$/.test(value)) {
|
|
64
|
-
const num = parseFloat(value);
|
|
65
|
-
return !isNaN(num) ? num : value;
|
|
66
|
-
}
|
|
67
|
-
return value;
|
|
68
|
-
} catch {
|
|
69
|
-
return value;
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
}, [envSource]);
|
|
73
|
-
|
|
74
|
-
// Get all environment variables and process them
|
|
75
|
-
const envResults = useMemo(() => {
|
|
76
|
-
const allEnvKeys = Object.keys(envSource);
|
|
77
|
-
const filteredKeys = allEnvKeys.filter(key => {
|
|
78
|
-
const value = envSource[key];
|
|
79
|
-
return envFilter(key, value);
|
|
80
|
-
});
|
|
81
|
-
return filteredKeys.map(key => ({
|
|
82
|
-
key,
|
|
83
|
-
data: getEnvValue(key)
|
|
84
|
-
}));
|
|
85
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
86
|
-
}, [envFilter, getEnvValue, envSource]);
|
|
87
|
-
return envResults;
|
|
88
|
-
}
|
|
1
|
+
"use strict";import{useMemo as o}from"react";import{getRemoteEnv as i}from"../utils/remoteEnv";export function useDynamicEnv({envFilter:s=()=>true}={}){const r=i()??(typeof process==="undefined"?{}:process.env);const u=o(()=>{return n=>{const t=r[n];if(t===void 0){return null}try{if(t.startsWith("{")||t.startsWith("[")){return JSON.parse(t)}if(t.toLowerCase()==="true")return true;if(t.toLowerCase()==="false")return false;if(/^\d+$/.test(t)){const e=parseInt(t,10);return!isNaN(e)?e:t}if(/^\d*\.\d+$/.test(t)){const e=parseFloat(t);return!isNaN(e)?e:t}return t}catch{return t}}},[r]);const a=o(()=>{const n=Object.keys(r);const t=n.filter(e=>{const c=r[e];return s(e,c)});return t.map(e=>({key:e,data:u(e)}))},[s,u,r]);return a}
|