@buoy-gg/env 7.0.41 → 7.0.43

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 (51) hide show
  1. package/README.md +11 -87
  2. package/lib/commonjs/env/components/EnvStatsOverview.js +1 -159
  3. package/lib/commonjs/env/components/EnvVarRow.js +1 -189
  4. package/lib/commonjs/env/components/EnvVarSection.js +1 -84
  5. package/lib/commonjs/env/components/EnvVarsModal.js +1 -322
  6. package/lib/commonjs/env/hooks/useDynamicEnv.js +1 -92
  7. package/lib/commonjs/env/types/index.js +1 -27
  8. package/lib/commonjs/env/types/types.js +1 -1
  9. package/lib/commonjs/env/types/userTypes.js +1 -1
  10. package/lib/commonjs/env/utils/envTypeDetector.js +1 -59
  11. package/lib/commonjs/env/utils/helpers.js +1 -119
  12. package/lib/commonjs/env/utils/index.js +1 -38
  13. package/lib/commonjs/env/utils/remoteEnv.js +1 -24
  14. package/lib/commonjs/env/utils/utils.js +1 -121
  15. package/lib/commonjs/index.js +1 -77
  16. package/lib/commonjs/preset.js +1 -81
  17. package/lib/commonjs/sync/envSyncAdapter.js +1 -51
  18. package/lib/module/env/components/EnvStatsOverview.js +1 -155
  19. package/lib/module/env/components/EnvVarRow.js +1 -185
  20. package/lib/module/env/components/EnvVarSection.js +1 -80
  21. package/lib/module/env/components/EnvVarsModal.js +1 -317
  22. package/lib/module/env/hooks/useDynamicEnv.js +1 -88
  23. package/lib/module/env/types/index.js +1 -4
  24. package/lib/module/env/types/types.js +1 -1
  25. package/lib/module/env/types/userTypes.js +1 -1
  26. package/lib/module/env/utils/envTypeDetector.js +1 -55
  27. package/lib/module/env/utils/helpers.js +1 -114
  28. package/lib/module/env/utils/index.js +1 -5
  29. package/lib/module/env/utils/remoteEnv.js +1 -19
  30. package/lib/module/env/utils/utils.js +1 -116
  31. package/lib/module/index.js +1 -21
  32. package/lib/module/preset.js +1 -78
  33. package/lib/module/sync/envSyncAdapter.js +1 -46
  34. package/lib/web/index.mjs +1 -2
  35. package/package.json +4 -7
  36. package/lib/typescript/env/components/EnvStatsOverview.d.ts.map +0 -1
  37. package/lib/typescript/env/components/EnvVarRow.d.ts.map +0 -1
  38. package/lib/typescript/env/components/EnvVarSection.d.ts.map +0 -1
  39. package/lib/typescript/env/components/EnvVarsModal.d.ts.map +0 -1
  40. package/lib/typescript/env/hooks/useDynamicEnv.d.ts.map +0 -1
  41. package/lib/typescript/env/types/index.d.ts.map +0 -1
  42. package/lib/typescript/env/types/types.d.ts.map +0 -1
  43. package/lib/typescript/env/types/userTypes.d.ts.map +0 -1
  44. package/lib/typescript/env/utils/envTypeDetector.d.ts.map +0 -1
  45. package/lib/typescript/env/utils/helpers.d.ts.map +0 -1
  46. package/lib/typescript/env/utils/index.d.ts.map +0 -1
  47. package/lib/typescript/env/utils/remoteEnv.d.ts.map +0 -1
  48. package/lib/typescript/env/utils/utils.d.ts.map +0 -1
  49. package/lib/typescript/index.d.ts.map +0 -1
  50. package/lib/typescript/preset.d.ts.map +0 -1
  51. package/lib/typescript/sync/envSyncAdapter.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,103 +1,27 @@
1
- # @buoy-gg/env
1
+ <div align="center">
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/@buoy-gg/env?style=flat-square&labelColor=1c1c1c&color=10B981)](https://www.npmjs.com/package/@buoy-gg/env) [![npm downloads](https://img.shields.io/npm/dm/@buoy-gg/env?style=flat-square&labelColor=1c1c1c&color=10B981)](https://www.npmjs.com/package/@buoy-gg/env)
3
+ # @buoy-gg/env
4
4
 
5
5
  Inspect the environment values available to your app at runtime and check required variables against expected types and values.
6
6
 
7
- Part of [Buoy](https://github.com/Buoy-gg/buoy). Install the package, complete account setup, and follow the integration steps below.
7
+ <a href="https://buoy.gg/buoy/latest/docs/tools/env"><b>Env guide</b></a> &nbsp;·&nbsp; <a href="https://buoy.gg">buoy.gg</a> &nbsp;·&nbsp; <a href="https://github.com/Buoy-gg/buoy">All tools</a>
8
8
 
9
- ![Environment Inspector demo](https://github.com/user-attachments/assets/75651046-33a0-4257-9011-3bcc4818a964)
9
+ [![npm version](https://img.shields.io/npm/v/@buoy-gg/env?style=flat-square&labelColor=10302a&color=2a9d78)](https://www.npmjs.com/package/@buoy-gg/env) [![npm downloads](https://img.shields.io/npm/dm/@buoy-gg/env?style=flat-square&labelColor=10302a&color=2a9d78&label=downloads%2Fmonth)](https://www.npmjs.com/package/@buoy-gg/env)
10
10
 
11
- ## Install
11
+ <a href="https://buoy.gg/buoy/latest/docs/tools/env"><img src="https://raw.githubusercontent.com/Buoy-gg/buoy/main/.github/readme/tools/env.png" alt="Watch the Env film on buoy.gg" width="640" /></a>
12
12
 
13
- ```bash
14
- npm install @buoy-gg/core @buoy-gg/env
15
- ```
16
-
17
- ## Before you start
13
+ </div>
18
14
 
19
- Use a development build with `@buoy-gg/core` and a Free or Pro Buoy account key. From your app’s directory, sign in:
15
+ ## Install
20
16
 
21
17
  ```bash
22
- npx --package=@buoy-gg/core buoy login
23
- ```
24
-
25
- For Expo, initialize Buoy before rendering the menu:
26
-
27
- ```tsx
28
- import { Buoy } from "@buoy-gg/core";
29
-
30
- Buoy.init({ licenseKey: process.env.EXPO_PUBLIC_BUOY_KEY });
31
- ```
32
-
33
- The login command writes the Expo key to `.env.local`. For React Native CLI, pass the key from your app’s environment configuration; React Native does not load `.env.local` automatically. Mount `FloatingDevTools` inside the same providers as your screens and restart the development server after installation. The [Quick Start](https://buoy.gg/buoy/latest/docs/quick-start) shows the complete root component setup.
34
-
35
- ## Quick start
36
-
37
- After initialization, render the menu in your app root:
38
-
39
- ```tsx
40
- import { FloatingDevTools } from "@buoy-gg/core";
41
-
42
- export default function App() {
43
- return (
44
- <>
45
- {/* your app */}
46
- <FloatingDevTools />
47
- </>
48
- );
49
- }
50
- ```
51
-
52
- To validate required variables, build the tool with `createEnvTool` and the fluent `envVar` builder, then pass it via `apps={[...]}`:
53
-
54
- ```tsx
55
- import { createEnvTool, envVar } from "@buoy-gg/env";
56
-
57
- const envTool = createEnvTool({
58
- requiredEnvVars: [
59
- envVar("EXPO_PUBLIC_API_URL").exists(),
60
- envVar("EXPO_PUBLIC_DEBUG_MODE").withType("boolean").build(),
61
- envVar("EXPO_PUBLIC_ENVIRONMENT").withValue("development").build(),
62
- envVar("EXPO_PUBLIC_MAX_RETRIES")
63
- .withType("number")
64
- .withDescription("Maximum API retry attempts")
65
- .build(),
66
- ],
67
- });
68
-
69
- // Render this menu inside your existing app root.
70
- <FloatingDevTools apps={[envTool]} />;
18
+ npm install @buoy-gg/core @buoy-gg/env
71
19
  ```
72
20
 
73
- ## Check the integration
74
-
75
- Open Env and check one known variable and one deliberately missing required variable. Expo statically replaces direct `process.env.EXPO_PUBLIC_NAME` references; enumerating `process.env` does not guarantee that those values are available. An empty list does not prove your app has no environment configuration.
21
+ Env appears in the menu once installed. To check required variables, build the tool with `createEnvTool` and pass it through `apps`.
76
22
 
77
- ## What you get
78
-
79
- - **Runtime discovery** — collects enumerable `EXPO_PUBLIC_` values available at runtime. Expo’s static inlining limits what enumeration can see.
80
- - **Required-variable validation** — declare which vars must exist, with expected values or types: `string`, `number`, `boolean`, `array`, `object`, `url`.
81
- - **Type detection** — values are auto-classified (string, number, boolean, array, object, url, json).
82
- - **Health score** — a 0-100% health percentage with HEALTHY / WARNING / ERROR / CRITICAL states, plus counts for total, required, missing, and wrong value/type.
83
- - **Per-variable status** — green (present and correct), yellow (wrong value or type), red (required but missing).
84
- - **Search and filters** — real-time search plus "All", "Missing", and "Issues" filters.
85
- - **Copy to clipboard** — copy any value with one tap.
86
-
87
- ## Desktop & AI
88
-
89
- To connect a React Native app to [Buoy Desktop](https://github.com/Buoy-gg/Buoy-Desktop) or MCP, install `@buoy-gg/external-sync` and follow the [Desktop connection guide](https://buoy.gg/buoy/latest/docs/desktop). Sign in to Desktop separately. [MCP setup](https://buoy.gg/buoy/latest/docs/mcp) also requires a process account and Pro access. Available remote actions depend on the tool and app integration.
90
-
91
- ## Account and plan requirements
92
-
93
- Use a Free or Pro Buoy account. History limits and paid features vary by tool; see [pricing](https://buoy.gg/pricing). Production access requires Pro where supported. Development-only hooks and actions remain unavailable in release builds.
23
+ The [Env guide](https://buoy.gg/buoy/latest/docs/tools/env) covers setup, what it captures and its limits. New to Buoy? Start with the [Quick start](https://buoy.gg/buoy/latest/docs/quick-start).
94
24
 
95
25
  ---
96
26
 
97
- 📚 [Full docs](https://buoy.gg/buoy/latest/docs/tools/env) · [All Buoy tools](https://github.com/Buoy-gg/buoy)
98
-
99
- Proprietary software. © Buoy LLC. [Terms](https://buoy.gg/terms)
100
-
101
- ## Web support (unreleased)
102
-
103
- Supply public runtime values explicitly with setRemoteEnv. The inspector cannot enumerate variables that a bundler replaced at build time. The browser build is available in this checkout and has not been published yet. See the [web setup guide](https://buoy.gg/buoy/latest/docs/web-preview) for registration, dependencies, and browser boundaries.
27
+ <sub>Part of <a href="https://buoy.gg">Buoy</a>, developer tools that live inside your app. Proprietary software. © Buoy LLC. <a href="https://buoy.gg/terms">Terms</a></sub>
@@ -1,159 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.EnvStatsOverview = EnvStatsOverview;
7
- var _reactNative = require("react-native");
8
- var _sharedUi = require("@buoy-gg/shared-ui");
9
- var _jsxRuntime = require("react/jsx-runtime");
10
- function EnvStatsOverview({
11
- stats,
12
- healthPercentage,
13
- healthStatus,
14
- healthColor,
15
- activeFilter = "all",
16
- onFilterChange
17
- }) {
18
- const issuesCount = stats.wrongValueCount + stats.wrongTypeCount;
19
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
20
- style: styles.container,
21
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CompactRow, {
22
- statusDotColor: healthColor,
23
- statusLabel: "System",
24
- statusSublabel: healthStatus.toLowerCase(),
25
- primaryText: "Environment Configuration",
26
- secondaryText: `${healthPercentage}% healthy`,
27
- testID: "buoy-env-health",
28
- statusSublabelTestID: "buoy-env-health-status",
29
- accessibilityLabel: `Environment configuration ${healthStatus.toLowerCase()}, ${healthPercentage}% healthy`,
30
- customBadge: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
31
- style: [styles.percentBadge, {
32
- borderColor: healthColor + "40",
33
- backgroundColor: healthColor + "10"
34
- }],
35
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
36
- style: [styles.percentText, {
37
- color: healthColor
38
- }],
39
- testID: "buoy-env-health-percent",
40
- children: [healthPercentage, "%"]
41
- })
42
- })
43
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
44
- style: styles.statsGrid,
45
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
46
- style: [styles.statCard, activeFilter === "all" && styles.statCardActive],
47
- onPress: () => onFilterChange?.("all"),
48
- activeOpacity: 0.8,
49
- testID: "buoy-env-filter-all",
50
- accessibilityRole: "button",
51
- accessibilityLabel: "Show all variables",
52
- accessibilityState: {
53
- selected: activeFilter === "all"
54
- },
55
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
56
- style: [styles.statValue, activeFilter === "all" && styles.statValueActive],
57
- testID: "buoy-env-filter-all-count",
58
- children: stats.requiredCount + stats.optionalCount
59
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
60
- style: [styles.statLabel, activeFilter === "all" && styles.statLabelActive],
61
- children: "ALL"
62
- })]
63
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
64
- style: [styles.statCard, activeFilter === "missing" && styles.statCardActive],
65
- onPress: () => onFilterChange?.("missing"),
66
- activeOpacity: 0.8,
67
- testID: "buoy-env-filter-missing",
68
- accessibilityRole: "button",
69
- accessibilityLabel: "Show missing variables",
70
- accessibilityState: {
71
- selected: activeFilter === "missing"
72
- },
73
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
74
- style: [styles.statValue, activeFilter === "missing" && styles.statValueActive],
75
- testID: "buoy-env-filter-missing-count",
76
- children: stats.missingCount
77
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
78
- style: [styles.statLabel, activeFilter === "missing" && styles.statLabelActive],
79
- children: "MISSING"
80
- })]
81
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
82
- style: [styles.statCard, activeFilter === "issues" && styles.statCardActive],
83
- onPress: () => onFilterChange?.("issues"),
84
- activeOpacity: 0.8,
85
- testID: "buoy-env-filter-issues",
86
- accessibilityRole: "button",
87
- accessibilityLabel: "Show variables with issues",
88
- accessibilityState: {
89
- selected: activeFilter === "issues"
90
- },
91
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
92
- style: [styles.statValue, activeFilter === "issues" && styles.statValueActive],
93
- testID: "buoy-env-filter-issues-count",
94
- children: issuesCount
95
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
96
- style: [styles.statLabel, activeFilter === "issues" && styles.statLabelActive],
97
- children: "ISSUES"
98
- })]
99
- })]
100
- })]
101
- });
102
- }
103
- const styles = _reactNative.StyleSheet.create({
104
- container: {
105
- gap: 8
106
- },
107
- statsGrid: {
108
- flexDirection: "row",
109
- gap: 8,
110
- paddingHorizontal: 4
111
- },
112
- statCard: {
113
- flex: 1,
114
- backgroundColor: _sharedUi.night.color.surface,
115
- borderRadius: 10,
116
- borderWidth: 1,
117
- borderColor: _sharedUi.night.color.border,
118
- padding: 12,
119
- alignItems: "center",
120
- justifyContent: "center",
121
- minHeight: 64
122
- },
123
- statCardActive: {
124
- borderColor: _sharedUi.night.color.accentBorderStrong,
125
- backgroundColor: _sharedUi.night.color.accentSoft
126
- },
127
- statValue: {
128
- fontSize: 22,
129
- fontWeight: "700",
130
- fontFamily: _sharedUi.monoFont,
131
- lineHeight: 26,
132
- color: _sharedUi.night.color.text
133
- },
134
- statValueActive: {
135
- color: _sharedUi.night.color.accent
136
- },
137
- statLabel: {
138
- fontSize: 9,
139
- color: _sharedUi.night.color.textTertiary,
140
- marginTop: 4,
141
- textTransform: "uppercase",
142
- letterSpacing: 0.5,
143
- fontWeight: "600"
144
- },
145
- statLabelActive: {
146
- color: _sharedUi.night.color.accent
147
- },
148
- percentBadge: {
149
- paddingHorizontal: 8,
150
- paddingVertical: 4,
151
- borderRadius: 6,
152
- borderWidth: 1
153
- },
154
- percentText: {
155
- fontSize: 14,
156
- fontWeight: "700",
157
- fontFamily: _sharedUi.monoFont
158
- }
159
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.EnvStatsOverview=EnvStatsOverview;var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _jsxRuntime=require("react/jsx-runtime");function EnvStatsOverview({stats:e,healthPercentage:i,healthStatus:o,healthColor:s,activeFilter:t="all",onFilterChange:a}){const r=e.wrongValueCount+e.wrongTypeCount;return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.container,children:[(0,_jsxRuntime.jsx)(_sharedUi.CompactRow,{statusDotColor:s,statusLabel:"System",statusSublabel:o.toLowerCase(),primaryText:"Environment Configuration",secondaryText:`${i}% healthy`,testID:"buoy-env-health",statusSublabelTestID:"buoy-env-health-status",accessibilityLabel:`Environment configuration ${o.toLowerCase()}, ${i}% healthy`,customBadge:(0,_jsxRuntime.jsx)(_reactNative.View,{style:[styles.percentBadge,{borderColor:s+"40",backgroundColor:s+"10"}],children:(0,_jsxRuntime.jsxs)(_reactNative.Text,{style:[styles.percentText,{color:s}],testID:"buoy-env-health-percent",children:[i,"%"]})})}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.statsGrid,children:[(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{style:[styles.statCard,t==="all"&&styles.statCardActive],onPress:()=>a?.("all"),activeOpacity:.8,testID:"buoy-env-filter-all",accessibilityRole:"button",accessibilityLabel:"Show all variables",accessibilityState:{selected:t==="all"},children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.statValue,t==="all"&&styles.statValueActive],testID:"buoy-env-filter-all-count",children:e.requiredCount+e.optionalCount}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.statLabel,t==="all"&&styles.statLabelActive],children:"ALL"})]}),(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{style:[styles.statCard,t==="missing"&&styles.statCardActive],onPress:()=>a?.("missing"),activeOpacity:.8,testID:"buoy-env-filter-missing",accessibilityRole:"button",accessibilityLabel:"Show missing variables",accessibilityState:{selected:t==="missing"},children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.statValue,t==="missing"&&styles.statValueActive],testID:"buoy-env-filter-missing-count",children:e.missingCount}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.statLabel,t==="missing"&&styles.statLabelActive],children:"MISSING"})]}),(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{style:[styles.statCard,t==="issues"&&styles.statCardActive],onPress:()=>a?.("issues"),activeOpacity:.8,testID:"buoy-env-filter-issues",accessibilityRole:"button",accessibilityLabel:"Show variables with issues",accessibilityState:{selected:t==="issues"},children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.statValue,t==="issues"&&styles.statValueActive],testID:"buoy-env-filter-issues-count",children:r}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.statLabel,t==="issues"&&styles.statLabelActive],children:"ISSUES"})]})]})]})}const styles=_reactNative.StyleSheet.create({container:{gap:8},statsGrid:{flexDirection:"row",gap:8,paddingHorizontal:4},statCard:{flex:1,backgroundColor:_sharedUi.night.color.surface,borderRadius:10,borderWidth:1,borderColor:_sharedUi.night.color.border,padding:12,alignItems:"center",justifyContent:"center",minHeight:64},statCardActive:{borderColor:_sharedUi.night.color.accentBorderStrong,backgroundColor:_sharedUi.night.color.accentSoft},statValue:{fontSize:22,fontWeight:"700",fontFamily:_sharedUi.monoFont,lineHeight:26,color:_sharedUi.night.color.text},statValueActive:{color:_sharedUi.night.color.accent},statLabel:{fontSize:9,color:_sharedUi.night.color.textTertiary,marginTop:4,textTransform:"uppercase",letterSpacing:.5,fontWeight:"600"},statLabelActive:{color:_sharedUi.night.color.accent},percentBadge:{paddingHorizontal:8,paddingVertical:4,borderRadius:6,borderWidth:1},percentText:{fontSize:14,fontWeight:"700",fontFamily:_sharedUi.monoFont}});
@@ -1,189 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.EnvVarRow = EnvVarRow;
7
- var _reactNative = require("react-native");
8
- var _sharedUi = require("@buoy-gg/shared-ui");
9
- var _envTypeDetector = require("../utils/envTypeDetector");
10
- var _jsxRuntime = require("react/jsx-runtime");
11
- const getStatusConfig = (status, expectedType) => {
12
- switch (status) {
13
- case "required_present":
14
- return {
15
- label: "Valid",
16
- color: _sharedUi.buoyColors.success,
17
- sublabel: "Required"
18
- };
19
- case "required_missing":
20
- return {
21
- label: "Missing",
22
- color: _sharedUi.buoyColors.error,
23
- sublabel: "Required"
24
- };
25
- case "required_wrong_value":
26
- return {
27
- label: "Wrong",
28
- color: _sharedUi.buoyColors.warning,
29
- sublabel: "Invalid value"
30
- };
31
- case "required_wrong_type":
32
- return {
33
- label: "Type Error",
34
- color: _sharedUi.buoyColors.info,
35
- sublabel: "Wrong type"
36
- };
37
- case "optional_present":
38
- return {
39
- label: "Set",
40
- color: _sharedUi.buoyColors.textSecondary,
41
- sublabel: "Optional"
42
- };
43
- }
44
- };
45
- const formatEnvKey = key => {
46
- // Format key similar to React Query: "section > subsection"
47
- return key.split("_").join(" › ");
48
- };
49
- const formatValue = value => {
50
- if (value === undefined || value === null) {
51
- return "undefined";
52
- }
53
- const str = typeof value === "string" ? value : String(value);
54
- return str;
55
- };
56
- function EnvVarRow({
57
- envVar,
58
- isExpanded,
59
- onPress
60
- }) {
61
- const config = getStatusConfig(envVar.status, envVar.expectedType);
62
- const hasValue = envVar.value !== undefined && envVar.value !== null;
63
-
64
- // Format primary text like React Query does: "section › subsection"
65
- // For env vars, we'll show the key formatted nicely
66
- const keyParts = envVar.key.split("_");
67
- const primaryText = keyParts.map(part => part.toLowerCase()).join(" › ");
68
-
69
- // The key is the row's identity everywhere a handle is needed. The visible
70
- // primary text is the prettified "expo › public › api › url" form, which is
71
- // no use to a test (or to VoiceOver) trying to find one specific variable.
72
- const rowID = `buoy-env-row-${envVar.key}`;
73
-
74
- // Create expanded content for value and expected value
75
- const expandedContent = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
76
- style: styles.expandedContainer,
77
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
78
- style: styles.expandedRow,
79
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
80
- style: styles.expandedLabel,
81
- children: "Value:"
82
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
83
- style: styles.expandedValue,
84
- numberOfLines: 3,
85
- testID: `${rowID}-value`,
86
- children: formatValue(envVar.value) || "undefined"
87
- })]
88
- }), envVar.status === "required_wrong_type" && envVar.expectedType && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
89
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
90
- style: styles.expandedRow,
91
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
92
- style: styles.expandedLabel,
93
- children: "Type:"
94
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TypeBadge, {
95
- type: (0, _envTypeDetector.getEnvVarType)(envVar.value),
96
- testID: `${rowID}-actual-type`
97
- })]
98
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
99
- style: styles.expandedRow,
100
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
101
- style: styles.expandedLabel,
102
- children: "Expected:"
103
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TypeBadge, {
104
- type: envVar.expectedType,
105
- testID: `${rowID}-expected-type`
106
- })]
107
- })]
108
- }), envVar.status === "required_wrong_value" && envVar.expectedValue && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
109
- style: styles.expandedRow,
110
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
111
- style: styles.expandedLabel,
112
- children: "Expected:"
113
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
114
- style: styles.expandedExpected,
115
- testID: `${rowID}-expected-value`,
116
- children: String(envVar.expectedValue)
117
- })]
118
- }), envVar.description && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
119
- style: styles.expandedRow,
120
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
121
- style: styles.expandedLabel,
122
- children: "Info:"
123
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
124
- style: styles.expandedDescription,
125
- testID: `${rowID}-description`,
126
- children: envVar.description
127
- })]
128
- })]
129
- });
130
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CompactRow, {
131
- statusDotColor: config.color,
132
- statusLabel: config.label,
133
- statusSublabel: config.sublabel,
134
- primaryText: primaryText,
135
- secondaryText: undefined // Don't show value inline anymore
136
- ,
137
- expandedContent: expandedContent,
138
- isExpanded: isExpanded,
139
- expandedGlowColor: config.color,
140
- customBadge: envVar.expectedType ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TypeBadge, {
141
- type: envVar.expectedType,
142
- testID: `${rowID}-type-badge`
143
- }) : undefined,
144
- showChevron: true,
145
- onPress: onPress ? () => onPress(envVar) : undefined,
146
- testID: rowID,
147
- statusTestID: `${rowID}-status`,
148
- statusSublabelTestID: `${rowID}-category`
149
- // The status is carried by a coloured dot and a two-word label that the
150
- // row's container merges away on Fabric — spelled out here so the verdict
151
- // reaches assistive tech (and Maestro) with the key it belongs to.
152
- ,
153
- accessibilityLabel: `${envVar.key}, ${config.label}, ${config.sublabel}`
154
- });
155
- }
156
- const styles = _reactNative.StyleSheet.create({
157
- expandedContainer: {
158
- gap: 6
159
- },
160
- expandedRow: {
161
- flexDirection: "row",
162
- alignItems: "center",
163
- gap: 8
164
- },
165
- expandedLabel: {
166
- fontSize: 10,
167
- color: _sharedUi.night.color.textTertiary,
168
- fontWeight: "600",
169
- minWidth: 60,
170
- fontFamily: _sharedUi.monoFont
171
- },
172
- expandedValue: {
173
- fontSize: 11,
174
- color: _sharedUi.night.color.textSecondary,
175
- fontFamily: _sharedUi.monoFont,
176
- flex: 1
177
- },
178
- expandedExpected: {
179
- fontSize: 11,
180
- color: _sharedUi.buoyColors.warning,
181
- fontFamily: _sharedUi.monoFont,
182
- flex: 1
183
- },
184
- expandedDescription: {
185
- fontSize: 11,
186
- color: _sharedUi.night.color.textSecondary,
187
- flex: 1
188
- }
189
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.EnvVarRow=EnvVarRow;var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _envTypeDetector=require("../utils/envTypeDetector");var _jsxRuntime=require("react/jsx-runtime");const getStatusConfig=(e,s)=>{switch(e){case"required_present":return{label:"Valid",color:_sharedUi.buoyColors.success,sublabel:"Required"};case"required_missing":return{label:"Missing",color:_sharedUi.buoyColors.error,sublabel:"Required"};case"required_wrong_value":return{label:"Wrong",color:_sharedUi.buoyColors.warning,sublabel:"Invalid value"};case"required_wrong_type":return{label:"Type Error",color:_sharedUi.buoyColors.info,sublabel:"Wrong type"};case"optional_present":return{label:"Set",color:_sharedUi.buoyColors.textSecondary,sublabel:"Optional"}}};const formatEnvKey=e=>{return e.split("_").join(" \u203A ")};const formatValue=e=>{if(e===void 0||e===null){return"undefined"}const s=typeof e==="string"?e:String(e);return s};function EnvVarRow({envVar:e,isExpanded:s,onPress:n}){const o=getStatusConfig(e.status,e.expectedType);const i=e.value!==void 0&&e.value!==null;const r=e.key.split("_");const l=r.map(a=>a.toLowerCase()).join(" \u203A ");const t=`buoy-env-row-${e.key}`;const d=(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.expandedContainer,children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.expandedRow,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.expandedLabel,children:"Value:"}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.expandedValue,numberOfLines:3,testID:`${t}-value`,children:formatValue(e.value)||"undefined"})]}),e.status==="required_wrong_type"&&e.expectedType&&(0,_jsxRuntime.jsxs)(_jsxRuntime.Fragment,{children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.expandedRow,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.expandedLabel,children:"Type:"}),(0,_jsxRuntime.jsx)(_sharedUi.TypeBadge,{type:(0,_envTypeDetector.getEnvVarType)(e.value),testID:`${t}-actual-type`})]}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.expandedRow,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.expandedLabel,children:"Expected:"}),(0,_jsxRuntime.jsx)(_sharedUi.TypeBadge,{type:e.expectedType,testID:`${t}-expected-type`})]})]}),e.status==="required_wrong_value"&&e.expectedValue&&(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.expandedRow,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.expandedLabel,children:"Expected:"}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.expandedExpected,testID:`${t}-expected-value`,children:String(e.expectedValue)})]}),e.description&&(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.expandedRow,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.expandedLabel,children:"Info:"}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.expandedDescription,testID:`${t}-description`,children:e.description})]})]});return(0,_jsxRuntime.jsx)(_sharedUi.CompactRow,{statusDotColor:o.color,statusLabel:o.label,statusSublabel:o.sublabel,primaryText:l,secondaryText:void 0,expandedContent:d,isExpanded:s,expandedGlowColor:o.color,customBadge:e.expectedType?(0,_jsxRuntime.jsx)(_sharedUi.TypeBadge,{type:e.expectedType,testID:`${t}-type-badge`}):void 0,showChevron:true,onPress:n?()=>n(e):void 0,testID:t,statusTestID:`${t}-status`,statusSublabelTestID:`${t}-category`,accessibilityLabel:`${e.key}, ${o.label}, ${o.sublabel}`})}const styles=_reactNative.StyleSheet.create({expandedContainer:{gap:6},expandedRow:{flexDirection:"row",alignItems:"center",gap:8},expandedLabel:{fontSize:10,color:_sharedUi.night.color.textTertiary,fontWeight:"600",minWidth:60,fontFamily:_sharedUi.monoFont},expandedValue:{fontSize:11,color:_sharedUi.night.color.textSecondary,fontFamily:_sharedUi.monoFont,flex:1},expandedExpected:{fontSize:11,color:_sharedUi.buoyColors.warning,fontFamily:_sharedUi.monoFont,flex:1},expandedDescription:{fontSize:11,color:_sharedUi.night.color.textSecondary,flex:1}});
@@ -1,84 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.EnvVarSection = EnvVarSection;
7
- var _react = require("react");
8
- var _reactNative = require("react-native");
9
- var _EnvVarRow = require("./EnvVarRow");
10
- var _sharedUi = require("@buoy-gg/shared-ui");
11
- var _jsxRuntime = require("react/jsx-runtime");
12
- function EnvVarSection({
13
- title,
14
- count,
15
- vars,
16
- emptyMessage
17
- }) {
18
- const [expandedVar, setExpandedVar] = (0, _react.useState)(null);
19
- const handleVarPress = (0, _react.useCallback)(envVar => {
20
- setExpandedVar(prev => prev === envVar.key ? null : envVar.key);
21
- }, []);
22
- if (vars.length === 0 && title === "Required Variables") {
23
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
24
- style: styles.sectionContainer,
25
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.SectionHeader, {
26
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Title, {
27
- children: title
28
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Badge, {
29
- count: 0,
30
- color: _sharedUi.night.color.accent
31
- })]
32
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
33
- style: styles.emptySection,
34
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
35
- style: styles.emptySectionText,
36
- children: emptyMessage
37
- })
38
- })]
39
- });
40
- }
41
- if (vars.length === 0) return null;
42
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
43
- style: styles.sectionContainer,
44
- children: [title !== "" && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.SectionHeader, {
45
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Title, {
46
- children: title
47
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Badge, {
48
- count: count,
49
- color: _sharedUi.night.color.accent
50
- })]
51
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
52
- style: styles.sectionContent,
53
- children: vars.map(envVar => /*#__PURE__*/(0, _jsxRuntime.jsx)(_EnvVarRow.EnvVarRow, {
54
- envVar: envVar,
55
- isExpanded: expandedVar === envVar.key,
56
- onPress: handleVarPress
57
- }, envVar.key))
58
- })]
59
- });
60
- }
61
- const styles = _reactNative.StyleSheet.create({
62
- sectionContainer: {
63
- gap: 8
64
- },
65
- sectionContent: {
66
- // No gap needed, EnvVarRow has its own margins
67
- },
68
- emptySection: {
69
- padding: 20,
70
- backgroundColor: _sharedUi.night.color.accent + "08",
71
- borderRadius: 8,
72
- borderWidth: 1,
73
- borderColor: _sharedUi.night.color.accent + "20",
74
- alignItems: "center"
75
- },
76
- emptySectionText: {
77
- color: _sharedUi.night.color.accent,
78
- fontSize: 11,
79
- textAlign: "center",
80
- fontFamily: _sharedUi.monoFont,
81
- opacity: 0.8,
82
- letterSpacing: 0.5
83
- }
84
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.EnvVarSection=EnvVarSection;var _react=require("react");var _reactNative=require("react-native");var _EnvVarRow=require("./EnvVarRow");var _sharedUi=require("@buoy-gg/shared-ui");var _jsxRuntime=require("react/jsx-runtime");function EnvVarSection({title:t,count:r,vars:n,emptyMessage:o}){const[i,c]=(0,_react.useState)(null);const a=(0,_react.useCallback)(e=>{c(s=>s===e.key?null:e.key)},[]);if(n.length===0&&t==="Required Variables"){return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.sectionContainer,children:[(0,_jsxRuntime.jsxs)(_sharedUi.SectionHeader,{children:[(0,_jsxRuntime.jsx)(_sharedUi.SectionHeader.Title,{children:t}),(0,_jsxRuntime.jsx)(_sharedUi.SectionHeader.Badge,{count:0,color:_sharedUi.night.color.accent})]}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.emptySection,children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.emptySectionText,children:o})})]})}if(n.length===0)return null;return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.sectionContainer,children:[t!==""&&(0,_jsxRuntime.jsxs)(_sharedUi.SectionHeader,{children:[(0,_jsxRuntime.jsx)(_sharedUi.SectionHeader.Title,{children:t}),(0,_jsxRuntime.jsx)(_sharedUi.SectionHeader.Badge,{count:r,color:_sharedUi.night.color.accent})]}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.sectionContent,children:n.map(e=>(0,_jsxRuntime.jsx)(_EnvVarRow.EnvVarRow,{envVar:e,isExpanded:i===e.key,onPress:a},e.key))})]})}const styles=_reactNative.StyleSheet.create({sectionContainer:{gap:8},sectionContent:{},emptySection:{padding:20,backgroundColor:_sharedUi.night.color.accent+"08",borderRadius:8,borderWidth:1,borderColor:_sharedUi.night.color.accent+"20",alignItems:"center"},emptySectionText:{color:_sharedUi.night.color.accent,fontSize:11,textAlign:"center",fontFamily:_sharedUi.monoFont,opacity:.8,letterSpacing:.5}});